Blender V5.0
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{
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. */
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);
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{
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 if (ref.sculpt_handle_.is_valid()) {
178 return ref.sculpt_handle_;
179 }
182 const float3 center = math::midpoint(bounds.min, bounds.max);
183 const float3 half_extent = bounds.max - center;
184 /* WORKAROUND: Instead of breaking const correctness everywhere, we only break it for this. */
185 const_cast<ObjectRef &>(ref).sculpt_handle_ = resource_handle(
186 ref, nullptr, &center, &half_extent);
187 return ref.sculpt_handle_;
188}
189
191{
192 bool freeze_culling = (USER_DEVELOPER_TOOL_TEST(&U, use_viewport_debug) && drw_get().v3d &&
194
195 BLI_assert_msg(view.manager_fingerprint_ != this->fingerprint_get(),
196 "Resources did not changed, no need to update");
197
198 view.manager_fingerprint_ = this->fingerprint_get();
199
200 view.bind();
201 view.compute_visibility(
202 bounds_buf.current(), infos_buf.current(), resource_len_, freeze_culling);
203}
204
206{
207 if (view.manager_fingerprint_ != this->fingerprint_get()) {
209 }
210}
211
213{
214 if (pass.is_empty()) {
215 return;
216 }
217
218 BLI_assert_msg((pass.manager_fingerprint_ != this->fingerprint_get()) ||
219 (pass.view_fingerprint_ != view.fingerprint_get()),
220 "Resources and view did not changed no need to update");
221 BLI_assert_msg((view.manager_fingerprint_ == this->fingerprint_get()) &&
222 (view.fingerprint_get() != 0),
223 "Resources or view changed, but compute_visibility was not called");
224
225 pass.manager_fingerprint_ = this->fingerprint_get();
226 pass.view_fingerprint_ = view.fingerprint_get();
227
228 pass.draw_commands_buf_.generate_commands(pass.headers_,
229 pass.commands_,
230 view.get_visibility_buffer(),
231 view.visibility_word_per_draw(),
232 view.view_len_,
233 pass.use_custom_ids);
234}
235
237{
238 if (pass.is_empty()) {
239 return;
240 }
241
242 pass.sort();
243 generate_commands(static_cast<PassMain &>(pass), view);
244}
245
247{
248 if (pass.is_empty()) {
249 return;
250 }
251
252 BLI_assert_msg(pass.manager_fingerprint_ != this->fingerprint_get(),
253 "Resources did not changed since last generate_command, no need to update");
254 pass.manager_fingerprint_ = this->fingerprint_get();
255
256 pass.draw_commands_buf_.generate_commands(pass.headers_, pass.commands_, pass.sub_passes_);
257}
258
260{
261 if (pass.is_empty()) {
262 return;
263 }
266}
267
269{
270 if (pass.is_empty()) {
271 return;
272 }
275}
276
278{
279 if (pass.is_empty()) {
280 return;
281 }
282
283 BLI_assert_msg(view.manager_fingerprint_ != 0, "compute_visibility was not called on this view");
284 BLI_assert_msg(view.manager_fingerprint_ == this->fingerprint_get(),
285 "Resources changed since last compute_visibility");
286 BLI_assert_msg(pass.manager_fingerprint_ != 0, "generate_command was not called on this pass");
287 BLI_assert_msg(pass.manager_fingerprint_ == this->fingerprint_get(),
288 "Resources changed since last generate_command");
289 /* The function generate_commands needs to be called for each view this pass is going to be
290 * submitted with. This is because the commands are stored inside the pass and not per view. */
291 BLI_assert_msg(pass.view_fingerprint_ == view.fingerprint_get(),
292 "View have changed since last generate_commands or "
293 "submitting with a different view");
294
295 debug_bind();
296
298 state.inverted_view = view.is_inverted();
299
300 view.bind();
301 pass.draw_commands_buf_.bind(state);
302
304
305 pass.submit(state);
306
307 state.cleanup();
308}
309
311{
312 if (pass.is_empty()) {
313 return;
314 }
315
316 if (view.manager_fingerprint_ != this->fingerprint_get()) {
318 }
319
320 if (pass.manager_fingerprint_ != this->fingerprint_get() ||
321 pass.view_fingerprint_ != view.fingerprint_get())
322 {
323 generate_commands(pass, view);
324 }
325
326 this->submit_only(pass, view);
327}
328
330{
331 if (pass.is_empty()) {
332 return;
333 }
334
335 pass.sort();
336
337 this->submit(static_cast<PassMain &>(pass), view);
338}
339
340void Manager::submit(PassSimple &pass, bool inverted_view)
341{
342 if (pass.is_empty()) {
343 return;
344 }
345
346 if (!pass.has_generated_commands()) {
347 generate_commands(pass);
348 }
349
350 debug_bind();
351
353 state.inverted_view = inverted_view;
354
355 pass.draw_commands_buf_.bind(state);
356
358
359 pass.submit(state);
360
361 state.cleanup();
362}
363
365{
366 if (pass.is_empty()) {
367 return;
368 }
369
370 debug_bind();
371
372 view.bind();
373
374 this->submit(pass, view.is_inverted());
375}
376
378{
379 submit(pass, view);
380
381 pass.draw_commands_buf_.resource_id_buf_.read();
382
384 output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
385 pass.draw_commands_buf_.resource_id_count_};
386 /* There is no visibility data for PassSimple. */
387 output.visibility = {(uint *)view.get_visibility_buffer().data(), 0};
388 return output;
389}
390
392{
393 submit(pass, view);
394
396
397 pass.draw_commands_buf_.resource_id_buf_.read();
398 view.get_visibility_buffer().read();
399
401 output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
402 pass.draw_commands_buf_.resource_id_count_};
403 output.visibility = {(uint *)view.get_visibility_buffer().data(),
404 divide_ceil_u(resource_len_, 32)};
405 return output;
406}
407
409{
410 matrix_buf.current().read();
411 bounds_buf.current().read();
412 infos_buf.current().read();
413
415 output.matrices = {matrix_buf.current().data(), resource_len_};
416 output.bounds = {bounds_buf.current().data(), resource_len_};
417 output.infos = {infos_buf.current().data(), resource_len_};
418 return output;
419}
420
421} // 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_DEVELOPER_TOOL_TEST(userdef, member)
@ V3D_DEBUG_FREEZE_CULLING
void DRW_submission_end()
void DRW_submission_start()
static AppView * view
void GPU_compute_dispatch(blender::gpu::Shader *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_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
void GPU_memory_barrier(GPUBarrier barrier)
Definition gpu_state.cc:326
void GPU_storagebuf_bind(blender::gpu::StorageBuf *ssbo, int slot)
void GPU_texture_free(blender::gpu::Texture *texture)
void GPU_uniformbuf_bind(blender::gpu::UniformBuf *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()
gpu::StorageBuf * 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)
ResourceHandleRange unique_handle_for_sculpt(const ObjectRef &ref)
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)
void warm_shader_specialization(PassMain &pass)
void submit_only(PassMain &pass, View &view)
void ensure_visibility(View &view)
SwapChain< ObjectMatricesBuf, 2 > matrix_buf
Vector< gpu::Texture * > acquired_textures
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:137
void warm_shader_specialization(command::RecordingState &state) const
Definition draw_pass.hh:699
SubPassVector< PassBase< DrawCommandBufType > > & sub_passes_
Definition draw_pass.hh:143
void submit(command::RecordingState &state) const
Definition draw_pass.hh:753
Vector< command::Undetermined, 0 > commands_
Definition draw_pass.hh:139
DrawCommandBufType & draw_commands_buf_
Definition draw_pass.hh:141
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
blender::gpu::Shader * DRW_shader_draw_resource_finalize_get()
#define output
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
int count
static ulong state[N]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1661
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