Blender V4.5
draw_manager.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_userdef_types.h"
10
11#include "BKE_paint.hh"
12#include "BKE_paint_bvh.hh"
13
14#include "BLI_math_base.h"
15#include "GPU_compute.hh"
16
18#include "draw_debug.hh"
19#include "draw_defines.hh"
20#include "draw_manager.hh"
21#include "draw_pass.hh"
22#include "draw_shader.hh"
23
24namespace blender::draw {
25
26std::atomic<uint32_t> Manager::global_sync_counter_ = 1;
27
29{
30 for (GPUTexture *texture : acquired_textures) {
31 /* Decrease refcount and free if 0. */
33 }
34}
35
36void Manager::begin_sync(Object *object_active)
37{
38 /* Add 2 to always have a non-null number even in case of overflow. */
39 sync_counter_ = (global_sync_counter_ += 2);
40
41 matrix_buf.swap();
42 bounds_buf.swap();
43 infos_buf.swap();
44
45 matrix_buf.current().trim_to_next_power_of_2(resource_len_);
46 bounds_buf.current().trim_to_next_power_of_2(resource_len_);
47 infos_buf.current().trim_to_next_power_of_2(resource_len_);
48 attributes_buf.trim_to_next_power_of_2(attribute_len_);
49
50 /* TODO: This means the reference is kept until further redraw or manager tear-down. Instead,
51 * they should be released after each draw loop. But for now, mimics old DRW behavior. */
52 for (GPUTexture *texture : acquired_textures) {
53 /* Decrease refcount and free if 0. */
55 }
56
57 acquired_textures.clear();
58 layer_attributes.clear();
59
60/* For some reason, if this uninitialized data pattern was enabled (ie release asserts enabled),
61 * The viewport just gives up rendering objects on ARM64 devices. Possibly Mesa GLOn12-related. */
62#if !defined(NDEBUG) && !defined(_M_ARM64)
63 /* Detect uninitialized data. */
64 memset(matrix_buf.current().data(),
65 0xF0,
66 matrix_buf.current().size() * sizeof(*matrix_buf.current().data()));
67 memset(bounds_buf.current().data(),
68 0xF0,
69 matrix_buf.current().size() * sizeof(*bounds_buf.current().data()));
70 memset(infos_buf.current().data(),
71 0xF0,
72 matrix_buf.current().size() * sizeof(*infos_buf.current().data()));
73#endif
74 resource_len_ = 0;
75 attribute_len_ = 0;
76 /* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */
77
78 this->object_active = object_active;
79
80 /* Init the 0 resource. */
82}
83
84void Manager::sync_layer_attributes()
85{
86 /* Sort the attribute IDs - the shaders use binary search. */
87 Vector<uint32_t> id_list;
88
89 id_list.reserve(layer_attributes.size());
90
91 for (uint32_t id : layer_attributes.keys()) {
92 id_list.append(id);
93 }
94
95 std::sort(id_list.begin(), id_list.end());
96
97 /* Look up the attributes. */
99
100 for (uint32_t id : id_list) {
101 if (layer_attributes_buf[count].sync(
102 drw_get().scene, drw_get().view_layer, layer_attributes.lookup(id)))
103 {
104 /* Check if the buffer is full. */
105 if (++count == size) {
106 break;
107 }
108 }
109 }
110
111 layer_attributes_buf[0].buffer_length = count;
112}
113
115{
116 GPU_debug_group_begin("Manager.end_sync");
117
118 sync_layer_attributes();
119
120 matrix_buf.current().push_update();
121 bounds_buf.current().push_update();
122 infos_buf.current().push_update();
123 attributes_buf.push_update();
124 layer_attributes_buf.push_update();
125
126 /* Useful for debugging the following resource finalize. But will trigger the drawing of the GPU
127 * debug draw/print buffers for every frame. Not nice for performance. */
128 // debug_bind();
129
131
132 /* Dispatch compute to finalize the resources on GPU. Save a bit of CPU time. */
133 uint thread_groups = divide_ceil_u(resource_len_, DRW_FINALIZE_GROUP_SIZE);
134 GPUShader *shader = DRW_shader_draw_resource_finalize_get();
135 GPU_shader_bind(shader);
136 GPU_shader_uniform_1i(shader, "resource_len", resource_len_);
137 GPU_storagebuf_bind(matrix_buf.current(), GPU_shader_get_ssbo_binding(shader, "matrix_buf"));
138 GPU_storagebuf_bind(bounds_buf.current(), GPU_shader_get_ssbo_binding(shader, "bounds_buf"));
139 GPU_storagebuf_bind(infos_buf.current(), GPU_shader_get_ssbo_binding(shader, "infos_buf"));
140 GPU_compute_dispatch(shader, thread_groups, 1, 1);
142
144
146}
147
149{
150 GPUStorageBuf *gpu_buf = DebugDraw::get().gpu_draw_buf_get();
151 if (gpu_buf == nullptr) {
152 return;
153 }
155#ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
156 /* Add a barrier to allow multiple shader writing to the same buffer. */
158#endif
159}
160
168
169uint64_t Manager::fingerprint_get()
170{
171 /* Covers new sync cycle, added resources and different #Manager. */
172 return sync_counter_ | (uint64_t(resource_len_) << 32);
173}
174
176{
177 /* TODO(fclem): Deduplicate with other engine. */
180 const float3 center = math::midpoint(bounds.min, bounds.max);
181 const float3 half_extent = bounds.max - center;
182 return resource_handle(ref, nullptr, &center, &half_extent);
183}
184
186{
187 bool freeze_culling = (USER_EXPERIMENTAL_TEST(&U, use_viewport_debug) && drw_get().v3d &&
189
190 BLI_assert_msg(view.manager_fingerprint_ != this->fingerprint_get(),
191 "Resources did not changed, no need to update");
192
193 view.manager_fingerprint_ = this->fingerprint_get();
194
195 view.bind();
196 view.compute_visibility(
197 bounds_buf.current(), infos_buf.current(), resource_len_, freeze_culling);
198}
199
201{
202 if (view.manager_fingerprint_ != this->fingerprint_get()) {
204 }
205}
206
208{
209 if (pass.is_empty()) {
210 return;
211 }
212
213 BLI_assert_msg((pass.manager_fingerprint_ != this->fingerprint_get()) ||
214 (pass.view_fingerprint_ != view.fingerprint_get()),
215 "Resources and view did not changed no need to update");
216 BLI_assert_msg((view.manager_fingerprint_ == this->fingerprint_get()) &&
217 (view.fingerprint_get() != 0),
218 "Resources or view changed, but compute_visibility was not called");
219
220 pass.manager_fingerprint_ = this->fingerprint_get();
221 pass.view_fingerprint_ = view.fingerprint_get();
222
223 pass.draw_commands_buf_.generate_commands(pass.headers_,
224 pass.commands_,
225 view.get_visibility_buffer(),
226 view.visibility_word_per_draw(),
227 view.view_len_,
228 pass.use_custom_ids);
229}
230
232{
233 if (pass.is_empty()) {
234 return;
235 }
236
237 pass.sort();
238 generate_commands(static_cast<PassMain &>(pass), view);
239}
240
242{
243 if (pass.is_empty()) {
244 return;
245 }
246
247 BLI_assert_msg(pass.manager_fingerprint_ != this->fingerprint_get(),
248 "Resources did not changed since last generate_command, no need to update");
249 pass.manager_fingerprint_ = this->fingerprint_get();
250
251 pass.draw_commands_buf_.generate_commands(pass.headers_, pass.commands_, pass.sub_passes_);
252}
253
255{
256 if (pass.is_empty()) {
257 return;
258 }
261}
262
264{
265 if (pass.is_empty()) {
266 return;
267 }
270}
271
273{
274 if (pass.is_empty()) {
275 return;
276 }
277
278 BLI_assert_msg(view.manager_fingerprint_ != 0, "compute_visibility was not called on this view");
279 BLI_assert_msg(view.manager_fingerprint_ == this->fingerprint_get(),
280 "Resources changed since last compute_visibility");
281 BLI_assert_msg(pass.manager_fingerprint_ != 0, "generate_command was not called on this pass");
282 BLI_assert_msg(pass.manager_fingerprint_ == this->fingerprint_get(),
283 "Resources changed since last generate_command");
284 /* The function generate_commands needs to be called for each view this pass is going to be
285 * submitted with. This is because the commands are stored inside the pass and not per view. */
286 BLI_assert_msg(pass.view_fingerprint_ == view.fingerprint_get(),
287 "View have changed since last generate_commands or "
288 "submitting with a different view");
289
290 debug_bind();
291
293 state.inverted_view = view.is_inverted();
294
295 view.bind();
296 pass.draw_commands_buf_.bind(state);
297
299
300 pass.submit(state);
301
302 state.cleanup();
303}
304
306{
307 if (pass.is_empty()) {
308 return;
309 }
310
311 if (view.manager_fingerprint_ != this->fingerprint_get()) {
313 }
314
315 if (pass.manager_fingerprint_ != this->fingerprint_get() ||
316 pass.view_fingerprint_ != view.fingerprint_get())
317 {
318 generate_commands(pass, view);
319 }
320
321 this->submit_only(pass, view);
322}
323
325{
326 if (pass.is_empty()) {
327 return;
328 }
329
330 pass.sort();
331
332 this->submit(static_cast<PassMain &>(pass), view);
333}
334
335void Manager::submit(PassSimple &pass, bool inverted_view)
336{
337 if (pass.is_empty()) {
338 return;
339 }
340
341 if (!pass.has_generated_commands()) {
342 generate_commands(pass);
343 }
344
345 debug_bind();
346
348 state.inverted_view = inverted_view;
349
350 pass.draw_commands_buf_.bind(state);
351
353
354 pass.submit(state);
355
356 state.cleanup();
357}
358
360{
361 if (pass.is_empty()) {
362 return;
363 }
364
365 debug_bind();
366
367 view.bind();
368
369 this->submit(pass, view.is_inverted());
370}
371
373{
374 submit(pass, view);
375
376 pass.draw_commands_buf_.resource_id_buf_.read();
377
379 output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
380 pass.draw_commands_buf_.resource_id_count_};
381 /* There is no visibility data for PassSimple. */
382 output.visibility = {(uint *)view.get_visibility_buffer().data(), 0};
383 return output;
384}
385
387{
388 submit(pass, view);
389
391
392 pass.draw_commands_buf_.resource_id_buf_.read();
393 view.get_visibility_buffer().read();
394
396 output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
397 pass.draw_commands_buf_.resource_id_count_};
398 output.visibility = {(uint *)view.get_visibility_buffer().data(),
399 divide_ceil_u(resource_len_, 32)};
400 return output;
401}
402
404{
405 matrix_buf.current().read();
406 bounds_buf.current().read();
407 infos_buf.current().read();
408
410 output.matrices = {matrix_buf.current().data(), resource_len_};
411 output.bounds = {bounds_buf.current().data(), resource_len_};
412 output.infos = {infos_buf.current().data(), resource_len_};
413 return output;
414}
415
416} // namespace blender::draw
A BVH for high poly meshes.
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE uint divide_ceil_u(uint a, uint b)
unsigned int uint
#define USER_EXPERIMENTAL_TEST(userdef, member)
@ V3D_DEBUG_FREEZE_CULLING
void DRW_submission_end()
void DRW_submission_start()
static AppView * view
void GPU_compute_dispatch(GPUShader *shader, uint groups_x_len, uint groups_y_len, uint groups_z_len, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(GPUShader *shader, const char *name)
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:385
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
void GPU_storagebuf_bind(GPUStorageBuf *ssbo, int slot)
void GPU_texture_free(GPUTexture *texture)
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
#define U
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
int64_t size() const
Definition BLI_map.hh:976
KeyIterator keys() const &
Definition BLI_map.hh:875
void append(const T &value)
void reserve(const int64_t min_capacity)
static DebugDraw & get()
GPUStorageBuf * gpu_draw_buf_get()
Definition draw_debug.cc:57
LayerAttributeBuf layer_attributes_buf
void begin_sync(Object *object_active=nullptr)
SwapChain< ObjectBoundsBuf, 2 > bounds_buf
SubmitDebugOutput submit_debug(PassSimple &pass, View &view)
void generate_commands(PassMain &pass, View &view)
SwapChain< ObjectInfosBuf, 2 > infos_buf
ObjectAttributeBuf attributes_buf
Map< uint32_t, GPULayerAttr > layer_attributes
void compute_visibility(View &view)
Vector< GPUTexture * > acquired_textures
void warm_shader_specialization(PassMain &pass)
void submit_only(PassMain &pass, View &view)
void ensure_visibility(View &view)
SwapChain< ObjectMatricesBuf, 2 > matrix_buf
ResourceHandleRange resource_handle_for_sculpt(const ObjectRef &ref)
void submit(PassSimple &pass, View &view)
ResourceHandleRange resource_handle(const ObjectRef &ref, float inflate_bounds=0.0f)
DataDebugOutput data_debug()
Vector< command::Header, 0 > headers_
Definition draw_pass.hh:134
void warm_shader_specialization(command::RecordingState &state) const
Definition draw_pass.hh:690
SubPassVector< PassBase< DrawCommandBufType > > & sub_passes_
Definition draw_pass.hh:140
void submit(command::RecordingState &state) const
Definition draw_pass.hh:744
Vector< command::Undetermined, 0 > commands_
Definition draw_pass.hh:136
DrawCommandBufType & draw_commands_buf_
Definition draw_pass.hh:138
DRWContext & drw_get()
Simple API to draw debug shapes and log in the viewport.
#define DRW_FINALIZE_GROUP_SIZE
#define DRW_DEBUG_DRAW_SLOT
#define DRW_OBJ_INFOS_SLOT
#define DRW_OBJ_ATTR_SLOT
#define DRW_OBJ_MAT_SLOT
#define DRW_LAYER_ATTR_UBO_SLOT
GPUShader * DRW_shader_draw_resource_finalize_get()
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
#define this
#define output
int count
static ulong state[N]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2912
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1477
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
T midpoint(const T &a, const T &b)
VecBase< float, 3 > float3
View3D * v3d
short debug_flag