Blender V4.3
draw_manager_shader.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10#include "DNA_object_types.h"
11#include "DNA_world_types.h"
12
13#include "BLI_dynstr.h"
14#include "BLI_listbase.h"
15#include "BLI_map.hh"
16#include "BLI_string_utils.hh"
17#include "BLI_threads.h"
18#include "BLI_time.h"
19
20#include "BKE_context.hh"
21#include "BKE_global.hh"
22#include "BKE_main.hh"
23
25
26#include "GPU_capabilities.hh"
27#include "GPU_material.hh"
28#include "GPU_shader.hh"
29
30#include "WM_api.hh"
31#include "WM_types.hh"
32
33#include "wm_window.hh"
34
35#include "draw_manager_c.hh"
36
37#include <atomic>
38#include <condition_variable>
39#include <mutex>
40
43
44#define USE_DEFERRED_COMPILATION 1
45
46using namespace blender;
47
48/* -------------------------------------------------------------------- */
70
74{
75 static ListBase compilation_threadpool_ = {};
76 return compilation_threadpool_;
77}
78
80{
81 static DRWShaderCompiler compiler_data_ = {};
82 return compiler_data_;
83}
84
86{
87 using namespace blender;
88
89 void *system_gpu_context = compiler_data().system_gpu_context;
90 GPUContext *blender_gpu_context = compiler_data().blender_gpu_context;
91 BLI_assert(system_gpu_context != nullptr);
92 BLI_assert(blender_gpu_context != nullptr);
94 WM_system_gpu_context_activate(system_gpu_context);
95 GPU_context_active_set(blender_gpu_context);
96
97 const bool use_parallel_compilation = GPU_use_parallel_compilation();
98 Vector<GPUMaterial *> async_mats;
99
100 while (true) {
101 if (compiler_data().stop) {
102 break;
103 }
104
105 compiler_data().queue_mutex.lock();
106 /* Pop last because it will be less likely to lock the main thread
107 * if all GPUMaterials are to be freed (see DRW_deferred_shader_remove()). */
108 GPUMaterial *mat = compiler_data().queue.is_empty() ? nullptr :
110 if (mat) {
111 /* Avoid another thread freeing the material mid compilation. */
113 }
114 compiler_data().queue_mutex.unlock();
115
116 if (mat) {
117 /* We have a new material that must be compiled,
118 * we either compile it directly or add it to the async compilation list. */
119 if (use_parallel_compilation) {
121 async_mats.append(mat);
122 }
123 else {
126 }
127 }
128 else if (!async_mats.is_empty()) {
129 /* (only if use_parallel_compilation == true)
130 * Keep querying the requested materials until all of them are ready. */
131 async_mats.remove_if([](GPUMaterial *mat) {
134 return true;
135 }
136 return false;
137 });
138 }
139 else {
140 /* Check for Material Optimization job once there are no more
141 * shaders to compile. */
142 compiler_data().queue_mutex.lock();
143 /* Pop last because it will be less likely to lock the main thread
144 * if all GPUMaterials are to be freed (see DRW_deferred_shader_remove()). */
146 nullptr :
148 if (optimize_mat) {
149 /* Avoid another thread freeing the material during optimization. */
150 GPU_material_acquire(optimize_mat);
151 }
152 compiler_data().queue_mutex.unlock();
153
154 if (optimize_mat) {
155 /* Compile optimized material shader. */
156 GPU_material_optimize(optimize_mat);
157 GPU_material_release(optimize_mat);
158 }
159 else {
160 /* No more materials to optimize, or shaders to compile. */
161 std::unique_lock lock(compiler_data().queue_mutex);
163 }
164 }
165
167 GPU_flush();
168 }
169 }
170
171 /* We have to wait until all the requested batches are ready,
172 * even if compiler_data().stop is true. */
173 while (!async_mats.is_empty()) {
174 async_mats.remove_if([](GPUMaterial *mat) {
177 return true;
178 }
179 return false;
180 });
181 }
182
183 GPU_context_active_set(nullptr);
184 WM_system_gpu_context_release(system_gpu_context);
186
187 return nullptr;
188}
189
191{
193 /* Deferred compilation is not supported. */
194 return;
195 }
196 static bool initialized = false;
197 if (initialized) {
199 return;
200 }
201 initialized = true;
202
203 compiler_data().stop = false;
204
207 compiler_data().system_gpu_context);
208 GPU_context_active_set(nullptr);
211
214}
215
217{
219 /* Deferred compilation is not supported. */
220 return;
221 }
222
223 compiler_data().stop = true;
224 compiler_data().queue_cv.notify_one();
226
227 /* Revert the queued state for the materials that has not been compiled.
228 * Note that this is not strictly needed since this function is called at program exit. */
229 {
230 std::scoped_lock queue_lock(compiler_data().queue_mutex);
231
232 while (!compiler_data().queue.is_empty()) {
234 }
235 while (!compiler_data().optimize_queue.is_empty()) {
236 GPU_material_optimization_status_set(compiler_data().optimize_queue.pop_last(),
238 }
239 }
240
241 WM_system_gpu_context_activate(compiler_data().system_gpu_context);
242 GPU_context_active_set(compiler_data().blender_gpu_context);
243 GPU_context_discard(compiler_data().blender_gpu_context);
244 WM_system_gpu_context_dispose(compiler_data().system_gpu_context);
245}
246
251static void drw_deferred_queue_append(GPUMaterial *mat, bool is_optimization_job)
252{
253 std::scoped_lock queue_lock(compiler_data().queue_mutex);
254
255 /* Add to either compilation or optimization queue. */
256 if (is_optimization_job) {
260 }
261 else {
264 }
265
266 compiler_data().queue_cv.notify_one();
267}
268
269static void drw_deferred_shader_add(GPUMaterial *mat, bool deferred)
270{
272 return;
273 }
274
276 deferred = false;
277 }
278
279 if (!deferred) {
281 /* Shaders could already be compiling. Have to wait for compilation to finish. */
282 while (GPU_material_status(mat) == GPU_MAT_QUEUED) {
284 }
287 }
288 return;
289 }
290
291 /* Don't add material to the queue twice. */
293 return;
294 }
295
296 /* Add deferred shader compilation to queue. */
297 drw_deferred_queue_append(mat, false);
298}
299
301{
302 const ListBase *attrs = GPU_material_layer_attributes(mat);
303
304 if (!attrs) {
305 return;
306 }
307
310
311 LISTBASE_FOREACH (GPULayerAttr *, attr, attrs) {
312 GPULayerAttr **p_val;
313
314 /* Add to the table and list if newly seen. */
315 if (!BLI_ghash_ensure_p(hash, POINTER_FROM_UINT(attr->hash_code), (void ***)&p_val)) {
317
318 GPULayerAttr *new_link = *p_val = static_cast<GPULayerAttr *>(MEM_dupallocN(attr));
319
320 /* Insert into the list ensuring sorted order. */
321 GPULayerAttr *link = static_cast<GPULayerAttr *>(list->first);
322
323 while (link && link->hash_code <= attr->hash_code) {
324 link = link->next;
325 }
326
327 new_link->prev = new_link->next = nullptr;
328 BLI_insertlinkbefore(list, link, new_link);
329 }
330
331 /* Reset the unused frames counter. */
332 (*p_val)->users = 0;
333 }
334}
335
337{
339 /* Deferred compilation is not supported. */
340 return;
341 }
342
343 std::scoped_lock queue_lock(compiler_data().queue_mutex);
344
345 /* Search for compilation job in queue. */
346 if (compiler_data().queue.contains(mat)) {
349 }
350
351 /* Search for optimization job in queue. */
352 if (compiler_data().optimize_queue.contains(mat)) {
355 }
356}
357
359{
361 /* Deferred compilation is not supported. */
362 return;
363 }
364
365 std::scoped_lock queue_lock(compiler_data().queue_mutex);
366
367 /* Search for optimization job in queue. */
368 if (compiler_data().optimize_queue.contains(mat)) {
371 }
372}
373
376/* -------------------------------------------------------------------- */
377
381 bNodeTree *ntree,
382 eGPUMaterialEngine engine,
383 const uint64_t shader_id,
384 const bool is_volume_shader,
385 bool deferred,
387 void *thunk)
388{
391 nullptr,
392 ntree,
393 &wo->gpumaterial,
394 wo->id.name,
395 engine,
396 shader_id,
397 is_volume_shader,
398 false,
399 callback,
400 thunk);
401
403
405 /* Do not deferred if doing render. */
406 deferred = false;
407 }
408
409 drw_deferred_shader_add(mat, deferred);
411 return mat;
412}
413
415 bNodeTree *ntree,
416 eGPUMaterialEngine engine,
417 const uint64_t shader_id,
418 const bool is_volume_shader,
419 bool deferred,
421 void *thunk,
422 GPUMaterialPassReplacementCallbackFn pass_replacement_cb)
423{
426 ma,
427 ntree,
428 &ma->gpumaterial,
429 ma->id.name,
430 engine,
431 shader_id,
432 is_volume_shader,
433 false,
434 callback,
435 thunk,
436 pass_replacement_cb);
437
439
440 drw_deferred_shader_add(mat, deferred);
442 return mat;
443}
444
446{
447 /* Do not perform deferred optimization if performing render.
448 * De-queue any queued optimization jobs. */
451 /* Remove from pending optimization job queue. */
453 /* If optimization job had already started, wait for it to complete. */
456 }
457 }
458 return;
459 }
460
461 /* We do not need to perform optimization on the material if it is already compiled or in the
462 * optimization queue. If optimization is not required, the status will be flagged as
463 * `GPU_MAT_OPTIMIZATION_SKIP`.
464 * We can also skip cases which have already been queued up. */
469 {
470 return;
471 }
472
473 /* Only queue optimization once the original shader has been successfully compiled. */
475 return;
476 }
477
478 /* Defer optimization until sufficient time has passed beyond creation. This avoids excessive
479 * recompilation for shaders which are being actively modified. */
481 return;
482 }
483
484 /* Add deferred shader compilation to queue. */
485 drw_deferred_queue_append(mat, true);
486}
487
489{
490 GPU_shader_free(shader);
491}
492
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
A dynamically sized string ADT.
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:752
#define LISTBASE_FOREACH(type, var, list)
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:370
void BLI_threadpool_init(struct ListBase *threadbase, void *(*do_thread)(void *), int tot)
Definition threads.cc:121
void BLI_threadpool_end(struct ListBase *threadbase)
Definition threads.cc:234
void BLI_threadpool_insert(struct ListBase *threadbase, void *callerdata)
Definition threads.cc:184
Platform independent time functions.
void BLI_time_sleep_ms(int ms)
Definition time.c:85
#define ELEM(...)
#define POINTER_FROM_UINT(i)
ID * DEG_get_original_id(ID *id)
Object is a sort of wrapper for general info.
bool GPU_use_parallel_compilation()
bool GPU_use_main_context_workaround()
void GPU_render_end()
GPUContext * GPU_context_create(void *ghost_window, void *ghost_context)
void GPU_render_begin()
void GPU_context_discard(GPUContext *)
void GPU_context_active_set(GPUContext *)
bool GPU_material_async_try_finalize(GPUMaterial *mat)
void GPU_material_acquire(GPUMaterial *mat)
void GPU_material_compile(GPUMaterial *mat)
void GPU_material_async_compile(GPUMaterial *mat)
void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status)
const ListBase * GPU_material_layer_attributes(const GPUMaterial *material)
eGPUMaterialEngine
void GPU_material_optimize(GPUMaterial *mat)
bool GPU_material_optimization_ready(GPUMaterial *mat)
void GPU_material_release(GPUMaterial *mat)
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
GPUMaterial * GPU_material_from_nodetree(Scene *scene, Material *ma, bNodeTree *ntree, ListBase *gpumaterials, const char *name, eGPUMaterialEngine engine, uint64_t shader_uuid, bool is_volume_shader, bool is_lookdev, GPUCodegenCallbackFn callback, void *thunk, GPUMaterialPassReplacementCallbackFn pass_replacement_cb=nullptr)
@ GPU_MAT_QUEUED
@ GPU_MAT_FAILED
@ GPU_MAT_SUCCESS
@ GPU_MAT_CREATED
void(*)(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen) GPUCodegenCallbackFn
@ GPU_MAT_OPTIMIZATION_READY
@ GPU_MAT_OPTIMIZATION_QUEUED
@ GPU_MAT_OPTIMIZATION_SUCCESS
@ GPU_MAT_OPTIMIZATION_SKIP
GPUPass *(*)(void *thunk, GPUMaterial *mat) GPUMaterialPassReplacementCallbackFn
void GPU_material_optimization_status_set(GPUMaterial *mat, eGPUMaterialOptimizationStatus status)
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat)
@ GPU_DRIVER_ANY
bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend)
@ GPU_OS_ANY
@ GPU_DEVICE_ANY
void GPU_shader_free(GPUShader *shader)
void GPU_flush()
Definition gpu_state.cc:294
volatile int lock
struct GPUContext GPUContext
struct GPUShader GPUShader
int64_t remove_if(Predicate &&predicate)
void append(const T &value)
bool is_empty() const
void remove_first_occurrence_and_reorder(const T &value)
DEGForeachIDComponentCallback callback
DRWManager DST
bool DRW_state_is_image_render()
void DRW_shader_exit()
char datatoc_gpu_shader_depth_only_frag_glsl[]
GPUMaterial * DRW_shader_from_world(World *wo, bNodeTree *ntree, eGPUMaterialEngine engine, const uint64_t shader_id, const bool is_volume_shader, bool deferred, GPUCodegenCallbackFn callback, void *thunk)
void DRW_shader_free(GPUShader *shader)
void DRW_shader_queue_optimize_material(GPUMaterial *mat)
static void drw_deferred_queue_append(GPUMaterial *mat, bool is_optimization_job)
void DRW_deferred_shader_optimize_remove(GPUMaterial *mat)
char datatoc_common_fullscreen_vert_glsl[]
static void * drw_deferred_shader_compilation_exec(void *)
GPUMaterial * DRW_shader_from_material(Material *ma, bNodeTree *ntree, eGPUMaterialEngine engine, const uint64_t shader_id, const bool is_volume_shader, bool deferred, GPUCodegenCallbackFn callback, void *thunk, GPUMaterialPassReplacementCallbackFn pass_replacement_cb)
static ListBase & compilation_threadpool()
static DRWShaderCompiler & compiler_data()
void DRW_shader_init()
static void drw_deferred_shader_add(GPUMaterial *mat, bool deferred)
static void drw_register_shader_vlattrs(GPUMaterial *mat)
static bool initialized
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
#define hash
Definition noise.c:154
unsigned __int64 uint64_t
Definition stdint.h:90
GHash * vlattrs_name_cache
ListBase vlattrs_name_list
bool vlattrs_ubo_ready
DRWContextState draw_ctx
GPUContext * blender_gpu_context
DRWData * vmempool
void * system_gpu_context
Vector< GPUMaterial * > queue
GPUContext * blender_gpu_context
std::atomic< bool > stop
std::condition_variable queue_cv
Vector< GPUMaterial * > optimize_queue
GPULayerAttr * next
uint32_t hash_code
GPULayerAttr * prev
char name[66]
Definition DNA_ID.h:425
ListBase gpumaterial
ListBase gpumaterial
void * DRW_deferred_shader_remove
Definition stubs.c:39
void * WM_system_gpu_context_create()
void WM_system_gpu_context_dispose(void *context)
void WM_system_gpu_context_activate(void *context)
void WM_system_gpu_context_release(void *context)