Blender V4.5
eevee_pipeline.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include "BLI_bounds.hh"
14#include "GPU_capabilities.hh"
15
16#include "eevee_instance.hh"
17#include "eevee_pipeline.hh"
18#include "eevee_shadow.hh"
19
20#include "GPU_debug.hh"
21
22#include "draw_common.hh"
23
24namespace blender::eevee {
25
26/* -------------------------------------------------------------------- */
31
33 const float background_opacity,
34 const float background_blur)
35{
36 Manager &manager = *inst_.manager;
37 RenderBuffers &rbufs = inst_.render_buffers;
38
39 world_ps_.init();
42 world_ps_.material_set(manager, gpumat);
43 world_ps_.push_constant("world_opacity_fade", background_opacity);
44 world_ps_.push_constant("world_background_blur", square_f(background_blur));
45 SphereProbeData &world_data = *static_cast<SphereProbeData *>(&inst_.light_probes.world_sphere_);
46 world_ps_.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
47 world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx);
48 /* RenderPasses & AOVs. */
49 world_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx);
50 world_ps_.bind_image("rp_value_img", &rbufs.rp_value_tx);
51 world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
52 /* Required by validation layers. */
53 world_ps_.bind_resources(inst_.cryptomatte);
54 world_ps_.bind_resources(inst_.uniform_data);
55 world_ps_.bind_resources(inst_.sampling);
56 world_ps_.bind_resources(inst_.sphere_probes);
57 world_ps_.bind_resources(inst_.volume_probes);
58 world_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
59 /* To allow opaque pass rendering over it. */
60 world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
61
62 clear_ps_.init();
63 clear_ps_.state_set(DRW_STATE_WRITE_COLOR);
64 clear_ps_.shader_set(inst_.shaders.static_shader_get(RENDERPASS_CLEAR));
65 /* RenderPasses & AOVs. Cleared by background (even if bad practice). */
66 clear_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx);
67 clear_ps_.bind_image("rp_value_img", &rbufs.rp_value_tx);
68 clear_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
69 /* Required by validation layers. */
70 clear_ps_.bind_resources(inst_.cryptomatte);
71 clear_ps_.bind_resources(inst_.uniform_data);
72 clear_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
73 /* To allow opaque pass rendering over it. */
74 clear_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
75}
76
78{
79 inst_.manager->submit(clear_ps_, view);
80}
81
83{
84 GPU_framebuffer_bind(combined_fb);
85 inst_.manager->submit(world_ps_, view);
86}
87
89
90/* -------------------------------------------------------------------- */
93
95{
96 const int2 extent(1);
99 dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent, usage);
100 dummy_renderpass_tx_.ensure_2d(GPU_RGBA16F, extent, usage);
101 dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent, 1, usage);
102 dummy_aov_value_tx_.ensure_2d_array(GPU_R16F, extent, 1, usage);
103
104 PassSimple &pass = cubemap_face_ps_;
105 pass.init();
107
108 Manager &manager = *inst_.manager;
109 pass.material_set(manager, gpumat);
110 pass.push_constant("world_opacity_fade", 1.0f);
111 pass.push_constant("world_background_blur", 0.0f);
112 pass.push_constant("world_coord_packed", int4(0.0f));
113 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
114 pass.bind_image("rp_normal_img", dummy_renderpass_tx_);
115 pass.bind_image("rp_light_img", dummy_renderpass_tx_);
116 pass.bind_image("rp_diffuse_color_img", dummy_renderpass_tx_);
117 pass.bind_image("rp_specular_color_img", dummy_renderpass_tx_);
118 pass.bind_image("rp_emission_img", dummy_renderpass_tx_);
119 pass.bind_image("rp_cryptomatte_img", dummy_cryptomatte_tx_);
120 pass.bind_image("rp_color_img", dummy_aov_color_tx_);
121 pass.bind_image("rp_value_img", dummy_aov_value_tx_);
122 pass.bind_image("aov_color_img", dummy_aov_color_tx_);
123 pass.bind_image("aov_value_img", dummy_aov_value_tx_);
124 pass.bind_ssbo("aov_buf", &inst_.film.aovs_info);
125 /* Required by validation layers. */
126 pass.bind_resources(inst_.cryptomatte);
127 pass.bind_resources(inst_.uniform_data);
128 pass.bind_resources(inst_.sampling);
129 pass.bind_resources(inst_.sphere_probes);
130 pass.bind_resources(inst_.volume_probes);
131 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
132}
133
135{
136 /* TODO(Miguel Pozo): All world probes are rendered as RAY_TYPE_GLOSSY. */
137 inst_.pipelines.data.is_sphere_probe = true;
138 inst_.uniform_data.push_update();
139
140 inst_.manager->submit(cubemap_face_ps_, view);
141
142 inst_.pipelines.data.is_sphere_probe = false;
143 inst_.uniform_data.push_update();
144}
145
147
148/* -------------------------------------------------------------------- */
152
154{
155 is_valid_ = (gpumat != nullptr) && (GPU_material_status(gpumat) == GPU_MAT_SUCCESS) &&
157 if (!is_valid_) {
158 /* Skip if the material has not compiled yet. */
159 return;
160 }
161
162 world_ps_.init();
163 world_ps_.state_set(DRW_STATE_WRITE_COLOR);
164 world_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
165 world_ps_.bind_resources(inst_.uniform_data);
166 world_ps_.bind_resources(inst_.volume.properties);
167 world_ps_.bind_resources(inst_.sampling);
168
169 world_ps_.material_set(*inst_.manager, gpumat);
170 /* Bind correct dummy texture for attributes defaults. */
171 PassSimple::Sub *sub = volume_sub_pass(world_ps_, nullptr, nullptr, gpumat);
172
173 is_valid_ = (sub != nullptr);
174 if (is_valid_) {
175 world_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
176 /* Sync with object property pass. */
177 world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
178 }
179}
180
182{
183 if (!is_valid_) {
184 /* Clear the properties buffer instead of rendering if there is no valid shader. */
185 inst_.volume.prop_scattering_tx_.clear(float4(0.0f));
186 inst_.volume.prop_extinction_tx_.clear(float4(0.0f));
187 inst_.volume.prop_emission_tx_.clear(float4(0.0f));
188 inst_.volume.prop_phase_tx_.clear(float4(0.0f));
189 inst_.volume.prop_phase_weight_tx_.clear(float4(0.0f));
190 return;
191 }
192
193 inst_.manager->submit(world_ps_, view);
194}
195
197
198/* -------------------------------------------------------------------- */
202
204{
205 render_ps_.init();
206
207 /* NOTE: TILE_COPY technique perform a three-pass implementation. First performing the clear
208 * directly on tile, followed by a fast depth-only pass, then storing the on-tile results into
209 * the shadow atlas during a final storage pass. This takes advantage of TBDR architecture,
210 * reducing overdraw and additional per-fragment calculations. */
211 bool shadow_update_tbdr = (ShadowModule::shadow_technique == ShadowTechnique::TILE_COPY);
212 if (shadow_update_tbdr) {
213 draw::PassMain::Sub &pass = render_ps_.sub("Shadow.TilePageClear");
215 pass.shader_set(inst_.shaders.static_shader_get(SHADOW_PAGE_TILE_CLEAR));
216 /* Only manually clear depth of the updated tiles.
217 * This is because the depth is initialized to near depth using attachments for fast clear and
218 * color is cleared to far depth. This way we can save a bit of bandwidth by only clearing
219 * the updated tiles depth to far depth and not touch the color attachment. */
221 pass.bind_ssbo("src_coord_buf", inst_.shadows.src_coord_buf_);
222 pass.draw_procedural_indirect(GPU_PRIM_TRIS, inst_.shadows.tile_draw_buf_);
223 }
224
225 {
226 /* Metal writes depth value in local tile memory, which is considered a color attachment. */
228
229 draw::PassMain::Sub &pass = render_ps_.sub("Shadow.Surface");
230 pass.state_set(state);
231 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
232 pass.bind_ssbo(SHADOW_RENDER_VIEW_BUF_SLOT, &inst_.shadows.render_view_buf_);
233 if (!shadow_update_tbdr) {
234 /* We do not need all of the shadow information when using the TBDR-optimized approach. */
235 pass.bind_image(SHADOW_ATLAS_IMG_SLOT, inst_.shadows.atlas_tx_);
236 pass.bind_ssbo(SHADOW_RENDER_MAP_BUF_SLOT, &inst_.shadows.render_map_buf_);
237 pass.bind_ssbo(SHADOW_PAGE_INFO_SLOT, &inst_.shadows.pages_infos_data_);
238 }
239 pass.bind_resources(inst_.uniform_data);
240 pass.bind_resources(inst_.sampling);
241 surface_double_sided_ps_ = &pass.sub("Shadow.Surface.Double-Sided");
242 surface_single_sided_ps_ = &pass.sub("Shadow.Surface.Single-Sided");
243 surface_single_sided_ps_->state_set(state | DRW_STATE_CULL_BACK);
244 }
245
246 if (shadow_update_tbdr) {
247 draw::PassMain::Sub &pass = render_ps_.sub("Shadow.TilePageStore");
248 pass.shader_set(inst_.shaders.static_shader_get(SHADOW_PAGE_TILE_STORE));
249 /* The most optimal way would be to only store pixels that have been rendered to (depth > 0).
250 * But that requires that the destination pages in the atlas would have been already cleared
251 * using compute. Experiments showed that it is faster to just copy the whole tiles back.
252 *
253 * For relative performance, raster-based clear within tile update adds around 0.1ms vs 0.25ms
254 * for compute based clear for a simple test case. */
256 /* Metal have implicit sync with Raster Order Groups. Other backend need to have manual
257 * sub-pass transition to allow reading the frame-buffer. This is a no-op on Metal. */
259 pass.bind_image(SHADOW_ATLAS_IMG_SLOT, inst_.shadows.atlas_tx_);
260 pass.bind_ssbo("dst_coord_buf", inst_.shadows.dst_coord_buf_);
261 pass.bind_ssbo("src_coord_buf", inst_.shadows.src_coord_buf_);
262 pass.draw_procedural_indirect(GPU_PRIM_TRIS, inst_.shadows.tile_draw_buf_);
263 }
264}
265
267{
269 surface_single_sided_ps_ :
270 surface_double_sided_ps_;
271 return &pass->sub(GPU_material_get_name(gpumat));
272}
273
275{
276 inst_.manager->submit(render_ps_, view);
277}
278
280
281/* -------------------------------------------------------------------- */
286
288{
289 camera_forward_ = inst_.camera.forward();
290 has_opaque_ = false;
291 has_transparent_ = false;
292
294 inst_.film.depth.test_state;
295 DRWState state_depth_color = state_depth_only | DRW_STATE_WRITE_COLOR;
296 {
297 prepass_ps_.init();
298
299 {
300 /* Common resources. */
301 prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
302 prepass_ps_.bind_resources(inst_.uniform_data);
303 prepass_ps_.bind_resources(inst_.velocity);
304 prepass_ps_.bind_resources(inst_.sampling);
305 }
306
307 prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
308 prepass_double_sided_static_ps_->state_set(state_depth_only);
309
310 prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
311 prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
312
313 prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
314 prepass_double_sided_moving_ps_->state_set(state_depth_color);
315
316 prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
317 prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
318 }
319 {
320 opaque_ps_.init();
321
322 {
323 /* Common resources. */
324 opaque_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
325 opaque_ps_.bind_resources(inst_.uniform_data);
326 opaque_ps_.bind_resources(inst_.lights);
327 opaque_ps_.bind_resources(inst_.shadows);
328 opaque_ps_.bind_resources(inst_.volume.result);
329 opaque_ps_.bind_resources(inst_.sampling);
330 opaque_ps_.bind_resources(inst_.hiz_buffer.front);
331 opaque_ps_.bind_resources(inst_.volume_probes);
332 opaque_ps_.bind_resources(inst_.sphere_probes);
333 }
334
335 opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided");
336 opaque_single_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_CLIP_CONTROL_UNIT_RANGE |
338
339 opaque_double_sided_ps_ = &opaque_ps_.sub("DoubleSided");
340 opaque_double_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_CLIP_CONTROL_UNIT_RANGE |
342 }
343 {
344 transparent_ps_.init();
345 /* Workaround limitation of PassSortable. Use dummy pass that will be sorted first in all
346 * circumstances. */
347 PassMain::Sub &sub = transparent_ps_.sub("ResourceBind", -FLT_MAX);
348
349 /* Common resources. */
350
351 /* Textures. */
352 sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
353
354 sub.bind_resources(inst_.uniform_data);
355 sub.bind_resources(inst_.lights);
356 sub.bind_resources(inst_.shadows);
357 sub.bind_resources(inst_.volume.result);
358 sub.bind_resources(inst_.sampling);
359 sub.bind_resources(inst_.hiz_buffer.front);
360 sub.bind_resources(inst_.volume_probes);
361 sub.bind_resources(inst_.sphere_probes);
362 }
363}
364
366 GPUMaterial *gpumat,
367 bool has_motion)
368{
370 "Forward Transparent should be registered directly without calling "
371 "PipelineModule::material_add()");
372 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
373 (has_motion ? prepass_single_sided_moving_ps_ :
374 prepass_single_sided_static_ps_) :
375 (has_motion ? prepass_double_sided_moving_ps_ :
376 prepass_double_sided_static_ps_);
377
378 /* If material is fully additive or transparent, we can skip the opaque prepass. */
379 /* TODO(fclem): To skip it, we need to know if the transparent BSDF is fully white AND if there
380 * is no mix shader (could do better constant folding but that's expensive). */
381
382 has_opaque_ = true;
383 return &pass->sub(GPU_material_get_name(gpumat));
384}
385
387{
389 "Forward Transparent should be registered directly without calling "
390 "PipelineModule::material_add()");
391 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_single_sided_ps_ :
392 opaque_double_sided_ps_;
393 has_opaque_ = true;
394 return &pass->sub(GPU_material_get_name(gpumat));
395}
396
398 ::Material *blender_mat,
399 GPUMaterial *gpumat)
400{
401 if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
402 return nullptr;
403 }
405 inst_.film.depth.test_state;
406 if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
408 }
409 has_transparent_ = true;
410 float sorting_value = math::dot(float3(ob->object_to_world().location()), camera_forward_);
411 PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
412 pass->state_set(state);
413 pass->material_set(*inst_.manager, gpumat, true);
414 return pass;
415}
416
418 ::Material *blender_mat,
419 GPUMaterial *gpumat)
420{
422 DRW_STATE_CLIP_CONTROL_UNIT_RANGE | inst_.film.depth.test_state;
423 if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
425 }
426 has_transparent_ = true;
427 float sorting_value = math::dot(float3(ob->object_to_world().location()), camera_forward_);
428 PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
429 pass->state_set(state);
430 pass->material_set(*inst_.manager, gpumat, true);
431 return pass;
432}
433
435 Framebuffer &prepass_fb,
436 Framebuffer &combined_fb,
437 int2 extent)
438{
439 if (!has_transparent_ && !has_opaque_) {
440 inst_.volume.draw_resolve(view);
441 return;
442 }
443
444 GPU_debug_group_begin("Forward.Opaque");
445
446 prepass_fb.bind();
447 inst_.manager->submit(prepass_ps_, view);
448
449 inst_.hiz_buffer.set_dirty();
450
451 inst_.shadows.set_view(view, extent);
452 inst_.volume_probes.set_view(view);
453 inst_.sphere_probes.set_view(view);
454
455 if (has_opaque_) {
456 combined_fb.bind();
457 inst_.manager->submit(opaque_ps_, view);
458 }
459
461
462 inst_.volume.draw_resolve(view);
463
464 if (has_transparent_) {
465 combined_fb.bind();
466 inst_.manager->submit(transparent_ps_, view);
467 }
468}
469
471
472/* -------------------------------------------------------------------- */
475
477{
478 gbuffer_ps_.init();
479 gbuffer_ps_.subpass_transition(GPU_ATTACHMENT_WRITE,
485 /* G-buffer. */
487 /* RenderPasses & AOVs. */
490 /* Cryptomatte. */
492 /* Storage Buffer. */
493 /* Textures. */
495
496 gbuffer_ps_.bind_resources(inst.uniform_data);
497 gbuffer_ps_.bind_resources(inst.sampling);
498 gbuffer_ps_.bind_resources(inst.hiz_buffer.front);
499 gbuffer_ps_.bind_resources(inst.cryptomatte);
500
501 /* Bind light resources for the NPR materials that gets rendered first.
502 * Non-NPR shaders will override these resource bindings. */
503 gbuffer_ps_.bind_resources(inst.lights);
504 gbuffer_ps_.bind_resources(inst.shadows);
505 gbuffer_ps_.bind_resources(inst.sphere_probes);
506 gbuffer_ps_.bind_resources(inst.volume_probes);
507
510
511 gbuffer_single_sided_hybrid_ps_ = &gbuffer_ps_.sub("DoubleSided");
513
514 gbuffer_double_sided_hybrid_ps_ = &gbuffer_ps_.sub("SingleSided");
516
517 gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided");
519
520 gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided");
522
524 closure_count_ = 0;
525}
526
528{
529 {
530 prepass_ps_.init();
531 /* Textures. */
532 prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
533
534 /* Make alpha hash scale sub-pixel so that it converges to a noise free image.
535 * If there is motion, use pixel scale for stability. */
536 bool alpha_hash_subpixel_scale = !inst_.is_viewport() || !inst_.velocity.camera_has_motion();
537 inst_.pipelines.data.alpha_hash_scale = alpha_hash_subpixel_scale ? 0.1f : 1.0f;
538
539 prepass_ps_.bind_resources(inst_.uniform_data);
540 prepass_ps_.bind_resources(inst_.velocity);
541 prepass_ps_.bind_resources(inst_.sampling);
542
544 inst_.film.depth.test_state;
546 inst_.film.depth.test_state | DRW_STATE_WRITE_COLOR;
547
548 prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
549 prepass_double_sided_static_ps_->state_set(state_depth_only);
550
551 prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
552 prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
553
554 prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
555 prepass_double_sided_moving_ps_->state_set(state_depth_color);
556
557 prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
558 prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
559 }
560
561 this->gbuffer_pass_sync(inst_);
562}
563
568
574
575void DeferredLayer::end_sync(bool is_first_pass,
576 bool is_last_pass,
577 bool next_layer_has_transmission)
578{
579 const bool has_any_closure = closure_bits_ != 0;
580 /* We need the feedback output in case of refraction in the next pass (see #126455). */
581 const bool is_layer_refracted = (next_layer_has_transmission && has_any_closure);
582 const bool has_transmit_closure = (closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_TRANSLUCENT));
583 const bool has_reflect_closure = (closure_bits_ & (CLOSURE_REFLECTION | CLOSURE_DIFFUSE));
584 use_raytracing_ = (has_transmit_closure || has_reflect_closure) &&
585 inst_.raytracing.use_raytracing();
586 use_clamp_direct_ = inst_.sampling.use_clamp_direct();
587 use_clamp_indirect_ = inst_.sampling.use_clamp_indirect();
588 /* Is the radiance split for the combined pass. */
589 use_split_radiance_ = use_raytracing_ || (use_clamp_direct_ || use_clamp_indirect_);
590
591 /* The first pass will never have any surfaces behind it. Nothing is refracted except the
592 * environment. So in this case, disable tracing and fallback to probe. */
593 use_screen_transmission_ = use_raytracing_ && has_transmit_closure && !is_first_pass;
594 use_screen_reflection_ = use_raytracing_ && has_reflect_closure;
595
596 use_feedback_output_ = (use_raytracing_ || is_layer_refracted) &&
597 (!is_last_pass || use_screen_reflection_);
598
599 {
600 RenderBuffersInfoData &rbuf_data = inst_.render_buffers.data;
601
602 /* Add the stencil classification step at the end of the GBuffer pass. */
603 {
604 GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_TILE_CLASSIFY);
605 PassMain::Sub &sub = gbuffer_ps_.sub("StencilClassify");
606 sub.subpass_transition(GPU_ATTACHMENT_WRITE, /* Needed for depth test. */
608 GPU_ATTACHMENT_READ, /* Header. */
612 sub.shader_set(sh);
614 /* Binding any buffer to satisfy the binding. The buffer is not actually used. */
615 sub.bind_ssbo("dummy_workaround_buf", &inst_.film.aovs_info);
616 }
619 /* The shader sets the stencil directly in one full-screen pass. */
620 sub.state_stencil(uint8_t(StencilBits::HEADER_BITS), /* Set by shader */ 0x0u, 0xFFu);
622 }
623 else {
624 /* The shader cannot set the stencil directly. So we do one full-screen pass for each
625 * stencil bit we need to set and accumulate the result. */
626 auto set_bit = [&](StencilBits bit) {
627 sub.push_constant("current_bit", int(bit));
628 sub.state_stencil(uint8_t(bit), 0xFFu, 0xFFu);
630 };
631
632 if (closure_count_ > 0) {
633 set_bit(StencilBits::CLOSURE_COUNT_0);
634 }
635 if (closure_count_ > 1) {
636 set_bit(StencilBits::CLOSURE_COUNT_1);
637 }
639 set_bit(StencilBits::TRANSMISSION);
640 }
641 }
642 }
643
644 {
645 PassSimple &pass = eval_light_ps_;
646 pass.init();
647
648 /* TODO(fclem): Could also skip if no material uses thickness from shadow. */
650 PassSimple::Sub &sub = pass.sub("Eval.ThicknessFromShadow");
651 sub.shader_set(inst_.shaders.static_shader_get(DEFERRED_THICKNESS_AMEND));
652 sub.bind_resources(inst_.lights);
653 sub.bind_resources(inst_.shadows);
654 sub.bind_resources(inst_.hiz_buffer.front);
655 sub.bind_resources(inst_.uniform_data);
656 sub.bind_resources(inst_.sampling);
657 sub.bind_texture("gbuf_header_tx", &inst_.gbuffer.header_tx);
658 sub.bind_image("gbuf_normal_img", &inst_.gbuffer.normal_tx);
660 /* Render where there is transmission and the thickness from shadow bit is set. */
661 uint8_t stencil_bits = uint8_t(StencilBits::TRANSMISSION) |
662 uint8_t(StencilBits::THICKNESS_FROM_SHADOW);
663 sub.state_stencil(0x0u, stencil_bits, stencil_bits);
666 }
667 {
668 const bool use_transmission = (closure_bits_ & CLOSURE_TRANSMISSION) != 0;
669 const bool use_split_indirect = do_split_direct_indirect_radiance(inst_);
670 const bool use_lightprobe_eval = do_merge_direct_indirect_eval(inst_);
671 PassSimple::Sub &sub = pass.sub("Eval.Light");
672 /* Use depth test to reject background pixels which have not been stencil cleared. */
673 /* WORKAROUND: Avoid rasterizer discard by enabling stencil write, but the shaders actually
674 * use no fragment output. */
676 sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
677 sub.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
678 sub.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
679 const ShadowSceneData &shadow_scene = inst_.shadows.get_data();
680 auto set_specialization_constants =
681 [&](PassSimple::Sub &sub, GPUShader *sh, bool use_transmission) {
682 sub.specialize_constant(sh, "render_pass_shadow_id", rbuf_data.shadow_id);
683 sub.specialize_constant(sh, "use_split_indirect", use_split_indirect);
684 sub.specialize_constant(sh, "use_lightprobe_eval", use_lightprobe_eval);
685 sub.specialize_constant(sh, "use_transmission", use_transmission);
686 sub.specialize_constant(sh, "shadow_ray_count", &shadow_scene.ray_count);
687 sub.specialize_constant(sh, "shadow_ray_step_count", &shadow_scene.step_count);
688 };
689 /* Submit the more costly ones first to avoid long tail in occupancy.
690 * See page 78 of "SIGGRAPH 2023: Unreal Engine Substrate" by Hillaire & de Rousiers. */
691
692 for (int i = min_ii(3, closure_count_) - 1; i >= 0; i--) {
693 GPUShader *sh = inst_.shaders.static_shader_get(eShaderType(DEFERRED_LIGHT_SINGLE + i));
694 set_specialization_constants(sub, sh, false);
695 sub.shader_set(sh);
696 sub.bind_image("direct_radiance_1_img", &direct_radiance_txs_[0]);
697 sub.bind_image("direct_radiance_2_img", &direct_radiance_txs_[1]);
698 sub.bind_image("direct_radiance_3_img", &direct_radiance_txs_[2]);
699 sub.bind_image("indirect_radiance_1_img", &indirect_result_.closures[0]);
700 sub.bind_image("indirect_radiance_2_img", &indirect_result_.closures[1]);
701 sub.bind_image("indirect_radiance_3_img", &indirect_result_.closures[2]);
702 sub.bind_resources(inst_.uniform_data);
703 sub.bind_resources(inst_.gbuffer);
704 sub.bind_resources(inst_.lights);
705 sub.bind_resources(inst_.shadows);
706 sub.bind_resources(inst_.sampling);
707 sub.bind_resources(inst_.hiz_buffer.front);
708 sub.bind_resources(inst_.sphere_probes);
709 sub.bind_resources(inst_.volume_probes);
710 uint8_t compare_mask = uint8_t(StencilBits::CLOSURE_COUNT_0) |
711 uint8_t(StencilBits::CLOSURE_COUNT_1) |
712 uint8_t(StencilBits::TRANSMISSION);
713 sub.state_stencil(0x0u, i + 1, compare_mask);
715 if (use_transmission) {
716 /* Separate pass for transmission BSDF as their evaluation is quite costly. */
717 set_specialization_constants(sub, sh, true);
718 sub.shader_set(sh);
719 sub.state_stencil(0x0u, (i + 1) | uint8_t(StencilBits::TRANSMISSION), compare_mask);
721 }
722 }
723 }
724 }
725 {
726 PassSimple &pass = combine_ps_;
727 pass.init();
728 GPUShader *sh = inst_.shaders.static_shader_get(DEFERRED_COMBINE);
729 /* TODO(fclem): Could specialize directly with the pass index but this would break it for
730 * OpenGL and Vulkan implementation which aren't fully supporting the specialize
731 * constant. */
732 pass.specialize_constant(sh,
733 "render_pass_diffuse_light_enabled",
734 (rbuf_data.diffuse_light_id != -1) ||
735 (rbuf_data.diffuse_color_id != -1));
736 pass.specialize_constant(sh,
737 "render_pass_specular_light_enabled",
738 (rbuf_data.specular_light_id != -1) ||
739 (rbuf_data.specular_color_id != -1));
740 pass.specialize_constant(sh, "use_split_radiance", use_split_radiance_);
742 sh, "use_radiance_feedback", use_feedback_output_ && use_clamp_direct_);
743 pass.specialize_constant(sh, "render_pass_normal_enabled", rbuf_data.normal_id != -1);
744 pass.shader_set(sh);
745 /* Use stencil test to reject pixels not written by this layer. */
747 /* Render where stencil is not 0. */
748 pass.state_stencil(0x0u, 0x0u, uint8_t(StencilBits::HEADER_BITS));
749 pass.bind_texture("direct_radiance_1_tx", &direct_radiance_txs_[0]);
750 pass.bind_texture("direct_radiance_2_tx", &direct_radiance_txs_[1]);
751 pass.bind_texture("direct_radiance_3_tx", &direct_radiance_txs_[2]);
752 pass.bind_texture("indirect_radiance_1_tx", &indirect_result_.closures[0]);
753 pass.bind_texture("indirect_radiance_2_tx", &indirect_result_.closures[1]);
754 pass.bind_texture("indirect_radiance_3_tx", &indirect_result_.closures[2]);
755 pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
756 pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
757 pass.bind_image("radiance_feedback_img", &radiance_feedback_tx_);
758 pass.bind_resources(inst_.gbuffer);
759 pass.bind_resources(inst_.uniform_data);
761 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
762 }
763 }
764}
765
767 GPUMaterial *gpumat,
768 bool has_motion)
769{
770 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
771 (has_motion ? prepass_single_sided_moving_ps_ :
773 (has_motion ? prepass_double_sided_moving_ps_ :
775
776 return &pass->sub(GPU_material_get_name(gpumat));
777}
778
780{
781 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
782 if (closure_bits == eClosureBits(0)) {
783 /* Fix the case where there is no active closure in the shader.
784 * In this case we force the evaluation of emission to avoid disabling the entire layer by
785 * accident, see #126459. */
786 closure_bits |= CLOSURE_EMISSION;
787 }
788 closure_bits_ |= closure_bits;
790
791 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
792 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
793 bool use_thickness_from_shadow = (blender_mat->blend_flag & MA_BL_THICKNESS_FROM_SHADOW) != 0;
794
795 PassMain::Sub *pass = (has_shader_to_rgba) ?
796 ((backface_culling) ? gbuffer_single_sided_hybrid_ps_ :
798 ((backface_culling) ? gbuffer_single_sided_ps_ :
800
801 PassMain::Sub *material_pass = &pass->sub(GPU_material_get_name(gpumat));
802 /* Set stencil for some deferred specialized shaders. */
803 uint8_t material_stencil_bits = 0u;
804 if (use_thickness_from_shadow) {
805 material_stencil_bits |= uint8_t(StencilBits::THICKNESS_FROM_SHADOW);
806 }
807 /* We use this opportunity to clear the stencil bits. The undefined areas are discarded using the
808 * gbuf header value. */
809 material_pass->state_stencil(0xFFu, material_stencil_bits, 0xFFu);
810
811 return material_pass;
812}
813
814GPUTexture *DeferredLayer::render(View &main_view,
815 View &render_view,
816 Framebuffer &prepass_fb,
817 Framebuffer &combined_fb,
818 Framebuffer &gbuffer_fb,
819 int2 extent,
820 RayTraceBuffer &rt_buffer,
821 GPUTexture *radiance_behind_tx)
822{
823 if (closure_count_ == 0) {
824 return nullptr;
825 }
826
827 RenderBuffers &rb = inst_.render_buffers;
828
829 constexpr eGPUTextureUsage usage_read = GPU_TEXTURE_USAGE_SHADER_READ;
830 constexpr eGPUTextureUsage usage_write = GPU_TEXTURE_USAGE_SHADER_WRITE;
831 constexpr eGPUTextureUsage usage_rw = usage_read | usage_write;
832
833 if (use_screen_transmission_) {
834 /* Update for refraction. */
835 inst_.hiz_buffer.update();
836 }
837
838 GPU_framebuffer_bind(prepass_fb);
839 inst_.manager->submit(prepass_ps_, render_view);
840
841 inst_.hiz_buffer.swap_layer();
842 /* Update for lighting pass or AO node. */
843 inst_.hiz_buffer.update();
844
845 inst_.volume_probes.set_view(render_view);
846 inst_.sphere_probes.set_view(render_view);
847 inst_.shadows.set_view(render_view, extent);
848
849 inst_.gbuffer.bind(gbuffer_fb);
850 inst_.manager->submit(gbuffer_ps_, render_view);
851
852 for (int i = 0; i < ARRAY_SIZE(direct_radiance_txs_); i++) {
853 direct_radiance_txs_[i].acquire(
854 (closure_count_ > i) ? extent : int2(1), DEFERRED_RADIANCE_FORMAT, usage_rw);
855 }
856
857 if (use_raytracing_) {
858 indirect_result_ = inst_.raytracing.render(
859 rt_buffer, radiance_behind_tx, closure_bits_, main_view, render_view);
860 }
861 else if (use_split_radiance_) {
862 indirect_result_ = inst_.raytracing.alloc_only(rt_buffer);
863 }
864 else {
865 indirect_result_ = inst_.raytracing.alloc_dummy(rt_buffer);
866 }
867
868 GPU_framebuffer_bind(combined_fb);
869 inst_.manager->submit(eval_light_ps_, render_view);
870
871 inst_.subsurface.render(
872 direct_radiance_txs_[0], indirect_result_.closures[0], closure_bits_, render_view);
873
874 radiance_feedback_tx_ = rt_buffer.feedback_ensure(!use_feedback_output_, extent);
875
876 if (use_feedback_output_ && use_clamp_direct_) {
877 /* We need to do a copy before the combine pass (otherwise we have a dependency issue) to save
878 * the emission and the previous layer's radiance. */
879 GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx);
880 }
881
882 GPU_framebuffer_bind(combined_fb);
883 inst_.manager->submit(combine_ps_);
884
885 if (use_feedback_output_ && !use_clamp_direct_) {
886 /* We skip writing the radiance during the combine pass. Do a simple fast copy. */
887 GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx);
888 }
889
890 indirect_result_.release();
891
892 for (int i = 0; i < ARRAY_SIZE(direct_radiance_txs_); i++) {
893 direct_radiance_txs_[i].release();
894 }
895
896 inst_.pipelines.deferred.debug_draw(render_view, combined_fb);
897
898 return use_feedback_output_ ? radiance_feedback_tx_ : nullptr;
899}
900
902
903/* -------------------------------------------------------------------- */
908
910{
911 Instance &inst = opaque_layer_.inst_;
912
913 const bool use_raytracing = (inst.scene->eevee.flag & SCE_EEVEE_SSR_ENABLED) != 0;
914 use_combined_lightprobe_eval = !use_raytracing;
915
916 opaque_layer_.begin_sync();
917 refraction_layer_.begin_sync();
918}
919
921{
922 opaque_layer_.end_sync(true, refraction_layer_.is_empty(), refraction_layer_.has_transmission());
923 refraction_layer_.end_sync(opaque_layer_.is_empty(), true, false);
924
925 debug_pass_sync();
926}
927
928void DeferredPipeline::debug_pass_sync()
929{
930 Instance &inst = opaque_layer_.inst_;
931 if (!ELEM(inst.debug_mode,
934 {
935 return;
936 }
937
938 PassSimple &pass = debug_draw_ps_;
939 pass.init();
941 pass.shader_set(inst.shaders.static_shader_get(DEBUG_GBUFFER));
942 pass.push_constant("debug_mode", int(inst.debug_mode));
943 pass.bind_resources(inst.gbuffer);
944 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
945}
946
947void DeferredPipeline::debug_draw(draw::View &view, GPUFrameBuffer *combined_fb)
948{
949 Instance &inst = opaque_layer_.inst_;
950 if (!ELEM(inst.debug_mode,
953 {
954 return;
955 }
956
957 switch (inst.debug_mode) {
959 inst.info_append("Debug Mode: Deferred Lighting Cost");
960 break;
962 inst.info_append("Debug Mode: Gbuffer Storage Cost");
963 break;
964 default:
965 /* Nothing to display. */
966 return;
967 }
968
969 GPU_framebuffer_bind(combined_fb);
970 inst.manager->submit(debug_draw_ps_, view);
971}
972
974 GPUMaterial *gpumat,
975 bool has_motion)
976{
977 if (!use_combined_lightprobe_eval && (blender_mat->blend_flag & MA_BL_SS_REFRACTION)) {
978 return refraction_layer_.prepass_add(blender_mat, gpumat, has_motion);
979 }
980 return opaque_layer_.prepass_add(blender_mat, gpumat, has_motion);
981}
982
984{
985 if (!use_combined_lightprobe_eval && (blender_mat->blend_flag & MA_BL_SS_REFRACTION)) {
986 return refraction_layer_.material_add(blender_mat, gpumat);
987 }
988 return opaque_layer_.material_add(blender_mat, gpumat);
989}
990
992 View &render_view,
993 Framebuffer &prepass_fb,
994 Framebuffer &combined_fb,
995 Framebuffer &gbuffer_fb,
996 int2 extent,
997 RayTraceBuffer &rt_buffer_opaque_layer,
998 RayTraceBuffer &rt_buffer_refract_layer)
999{
1000 GPUTexture *feedback_tx = nullptr;
1001
1002 GPU_debug_group_begin("Deferred.Opaque");
1003 feedback_tx = opaque_layer_.render(main_view,
1004 render_view,
1005 prepass_fb,
1006 combined_fb,
1007 gbuffer_fb,
1008 extent,
1009 rt_buffer_opaque_layer,
1010 feedback_tx);
1012
1013 GPU_debug_group_begin("Deferred.Refract");
1014 feedback_tx = refraction_layer_.render(main_view,
1015 render_view,
1016 prepass_fb,
1017 combined_fb,
1018 gbuffer_fb,
1019 extent,
1020 rt_buffer_refract_layer,
1021 feedback_tx);
1023}
1024
1026
1027/* -------------------------------------------------------------------- */
1031
1033{
1034 object_bounds_.clear();
1035 combined_screen_bounds_ = std::nullopt;
1036 use_hit_list = false;
1037 is_empty = true;
1038 finalized = false;
1039 has_scatter = false;
1040 has_absorption = false;
1041
1042 draw::PassMain &layer_pass = volume_layer_ps_;
1043 layer_pass.init();
1044 layer_pass.clear_stencil(0x0u);
1045 {
1046 PassMain::Sub &pass = layer_pass.sub("occupancy_ps");
1047 /* Always double sided to let all fragments be invoked. */
1049 pass.bind_resources(inst_.uniform_data);
1050 pass.bind_resources(inst_.volume.occupancy);
1051 pass.bind_resources(inst_.sampling);
1052 occupancy_ps_ = &pass;
1053 }
1054 {
1055 PassMain::Sub &pass = layer_pass.sub("material_ps");
1056 /* Double sided with stencil equal to ensure only one fragment is invoked per pixel. */
1058 pass.state_stencil(0x1u, 0x1u, 0x1u);
1060 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1061 pass.bind_resources(inst_.uniform_data);
1062 pass.bind_resources(inst_.volume.properties);
1063 pass.bind_resources(inst_.sampling);
1064 material_ps_ = &pass;
1065 }
1066}
1067
1069 const ::Material *blender_mat,
1070 GPUMaterial *gpumat)
1071{
1073 "Only volume material should be added here");
1074 bool use_fast_occupancy = (ob->type == OB_VOLUME) ||
1075 (blender_mat->volume_intersection_method == MA_VOLUME_ISECT_FAST);
1076 use_hit_list |= !use_fast_occupancy;
1077 is_empty = false;
1078
1079 PassMain::Sub *pass = &occupancy_ps_->sub(GPU_material_get_name(gpumat));
1080 pass->material_set(*inst_.manager, gpumat, true);
1081 pass->push_constant("use_fast_method", use_fast_occupancy);
1082 return pass;
1083}
1084
1086 const ::Material * /*blender_mat*/,
1087 GPUMaterial *gpumat)
1088{
1090 "Only volume material should be added here");
1092
1093 PassMain::Sub *pass = &material_ps_->sub(GPU_material_get_name(gpumat));
1094 pass->material_set(*inst_.manager, gpumat, true);
1096 has_scatter = true;
1097 }
1099 has_absorption = true;
1100 }
1101 return pass;
1102}
1103
1104bool VolumeLayer::bounds_overlaps(const VolumeObjectBounds &object_bounds) const
1105{
1106 /* First check the biggest area. */
1107 if (bounds::intersect(object_bounds.screen_bounds, combined_screen_bounds_)) {
1108 return true;
1109 }
1110 /* Check against individual bounds to try to squeeze the new object between them. */
1111 for (const std::optional<Bounds<float2>> &other_aabb : object_bounds_) {
1112 if (bounds::intersect(object_bounds.screen_bounds, other_aabb)) {
1113 return true;
1114 }
1115 }
1116 return false;
1117}
1118
1120{
1121 object_bounds_.append(object_bounds.screen_bounds);
1122 combined_screen_bounds_ = bounds::merge(combined_screen_bounds_, object_bounds.screen_bounds);
1123}
1124
1126{
1127 if (is_empty) {
1128 return;
1129 }
1130 if (finalized == false) {
1131 finalized = true;
1132 if (use_hit_list) {
1133 /* Add resolve pass only when needed. Insert after occupancy, before material pass. */
1134 occupancy_ps_->shader_set(inst_.shaders.static_shader_get(VOLUME_OCCUPANCY_CONVERT));
1135 occupancy_ps_->barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
1136 occupancy_ps_->draw_procedural(GPU_PRIM_TRIS, 1, 3);
1137 }
1138 }
1139 /* TODO(fclem): Move this clear inside the render pass. */
1140 occupancy_tx.clear(uint4(0u));
1141 inst_.manager->submit(volume_layer_ps_, view);
1142}
1143
1145
1146/* -------------------------------------------------------------------- */
1149
1151{
1152 object_integration_range_ = std::nullopt;
1153 has_scatter_ = false;
1154 has_absorption_ = false;
1155 for (auto &layer : layers_) {
1156 layer->sync();
1157 }
1158}
1159
1161{
1162 for (auto &layer : layers_) {
1163 layer->render(view, occupancy_tx);
1164 }
1165}
1166
1168{
1169 /* TODO(fclem): For panoramic camera, we will have to do this check for each cube-face. */
1170 const float4x4 &view_matrix = camera.data_get().viewmat;
1171 /* Note in practice we only care about the projection type since we only care about 2D overlap,
1172 * and this is independent of FOV. */
1173 const float4x4 &projection_matrix = camera.data_get().winmat;
1174
1175 const Bounds<float3> bounds = BKE_object_boundbox_get(ob).value_or(Bounds(float3(0.0f)));
1176
1177 BoundBox bb;
1179
1180 screen_bounds = std::nullopt;
1181 z_range = std::nullopt;
1182
1183 for (float3 l_corner : bb.vec) {
1184 float3 ws_corner = math::transform_point(ob->object_to_world(), l_corner);
1185 /* Split view and projection for precision. */
1186 float3 vs_corner = math::transform_point(view_matrix, ws_corner);
1187 float3 ss_corner = math::project_point(projection_matrix, vs_corner);
1188
1189 z_range = bounds::min_max(z_range, vs_corner.z);
1190 if (camera.is_perspective() && vs_corner.z >= 1.0e-8f) {
1191 /* If the object is crossing the z=0 plane, we can't determine its 2D bounds easily.
1192 * In this case, consider the object covering the whole screen.
1193 * Still continue the loop for the Z range. */
1194 screen_bounds = Bounds<float2>(float2(-1.0f), float2(1.0f));
1195 }
1196 else {
1198 }
1199 }
1200}
1201
1203{
1204 /* TODO(fclem): This is against design. Sync shouldn't depend on view properties (camera). */
1205 VolumeObjectBounds object_bounds(inst_.camera, ob);
1206 if (math::reduce_max(object_bounds.screen_bounds->size()) < 1e-5) {
1207 /* WORKAROUND(fclem): Fixes an issue with 0 scaled object (see #132889).
1208 * Is likely to be an issue somewhere else in the pipeline but it is hard to find. */
1209 return nullptr;
1210 }
1211
1212 object_integration_range_ = bounds::merge(object_integration_range_, object_bounds.z_range);
1213
1214 /* Do linear search in all layers in order. This can be optimized. */
1215 for (auto &layer : layers_) {
1216 if (!layer->bounds_overlaps(object_bounds)) {
1217 layer->add_object_bound(object_bounds);
1218 return layer.get();
1219 }
1220 }
1221 /* No non-overlapping layer found. Create new one. */
1222 int64_t index = layers_.append_and_get_index(std::make_unique<VolumeLayer>(inst_));
1223 (*layers_[index]).add_object_bound(object_bounds);
1224 return layers_[index].get();
1225}
1226
1227std::optional<Bounds<float>> VolumePipeline::object_integration_range() const
1228{
1229 return object_integration_range_;
1230}
1231
1233{
1234 for (const auto &layer : layers_) {
1235 if (layer->use_hit_list) {
1236 return true;
1237 }
1238 }
1239 return false;
1240}
1241
1243
1244/* -------------------------------------------------------------------- */
1249
1251{
1252 draw::PassMain &pass = opaque_layer_.prepass_ps_;
1253 pass.init();
1254 {
1255 /* Common resources. */
1256
1257 /* Textures. */
1258 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1259
1260 pass.bind_resources(inst_.uniform_data);
1261 pass.bind_resources(inst_.velocity);
1262 pass.bind_resources(inst_.sampling);
1263 }
1264
1266 inst_.film.depth.test_state;
1267 /* Only setting up static pass because we don't use motion vectors for light-probes. */
1268 opaque_layer_.prepass_double_sided_static_ps_ = &pass.sub("DoubleSided");
1269 opaque_layer_.prepass_double_sided_static_ps_->state_set(state_depth_only);
1270 opaque_layer_.prepass_single_sided_static_ps_ = &pass.sub("SingleSided");
1271 opaque_layer_.prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
1272
1273 opaque_layer_.gbuffer_pass_sync(inst_);
1274}
1275
1277{
1278 if (!opaque_layer_.prepass_ps_.is_empty()) {
1279 PassSimple &pass = eval_light_ps_;
1280 pass.init();
1281 /* Use depth test to reject background pixels. */
1283 pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_CAPTURE_EVAL));
1284 pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
1285 pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
1286 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1287 pass.bind_resources(inst_.uniform_data);
1288 pass.bind_resources(inst_.gbuffer);
1289 pass.bind_resources(inst_.lights);
1290 pass.bind_resources(inst_.shadows);
1291 pass.bind_resources(inst_.sampling);
1292 pass.bind_resources(inst_.hiz_buffer.front);
1293 pass.bind_resources(inst_.volume_probes);
1295 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
1296 }
1297}
1298
1300{
1301 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
1302 opaque_layer_.prepass_single_sided_static_ps_ :
1303 opaque_layer_.prepass_double_sided_static_ps_;
1304
1305 return &pass->sub(GPU_material_get_name(gpumat));
1306}
1307
1309{
1310 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
1311 if (closure_bits == eClosureBits(0)) {
1312 /* Fix the case where there is no active closure in the shader.
1313 * In this case we force the evaluation of emission to avoid disabling the entire layer by
1314 * accident, see #126459. */
1315 closure_bits |= CLOSURE_EMISSION;
1316 }
1317 opaque_layer_.closure_bits_ |= closure_bits;
1318 opaque_layer_.closure_count_ = max_ii(opaque_layer_.closure_count_, count_bits_i(closure_bits));
1319
1320 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
1321 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
1322
1323 PassMain::Sub *pass = (has_shader_to_rgba) ?
1324 ((backface_culling) ? opaque_layer_.gbuffer_single_sided_hybrid_ps_ :
1325 opaque_layer_.gbuffer_double_sided_hybrid_ps_) :
1326 ((backface_culling) ? opaque_layer_.gbuffer_single_sided_ps_ :
1327 opaque_layer_.gbuffer_double_sided_ps_);
1328
1329 return &pass->sub(GPU_material_get_name(gpumat));
1330}
1331
1333 Framebuffer &prepass_fb,
1334 Framebuffer &combined_fb,
1335 Framebuffer &gbuffer_fb,
1336 int2 extent)
1337{
1338 GPU_debug_group_begin("Probe.Render");
1339
1340 GPU_framebuffer_bind(prepass_fb);
1341 inst_.manager->submit(opaque_layer_.prepass_ps_, view);
1342
1343 inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
1344 inst_.hiz_buffer.update();
1345
1346 inst_.lights.set_view(view, extent);
1347 inst_.shadows.set_view(view, extent);
1348 inst_.volume_probes.set_view(view);
1349 inst_.sphere_probes.set_view(view);
1350
1351 /* Update for lighting pass. */
1352 inst_.hiz_buffer.update();
1353
1354 inst_.gbuffer.bind(gbuffer_fb);
1355 inst_.manager->submit(opaque_layer_.gbuffer_ps_, view);
1356
1357 GPU_framebuffer_bind(combined_fb);
1358 inst_.manager->submit(eval_light_ps_, view);
1359
1361}
1362
1364
1365/* -------------------------------------------------------------------- */
1369
1371{
1372 {
1373 prepass_ps_.init();
1374 prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1375 prepass_ps_.bind_ubo(CLIP_PLANE_BUF, inst_.planar_probes.world_clip_buf_);
1376 prepass_ps_.bind_resources(inst_.uniform_data);
1377 prepass_ps_.bind_resources(inst_.sampling);
1378
1380 inst_.film.depth.test_state;
1381
1382 prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
1383 prepass_double_sided_static_ps_->state_set(state_depth_only);
1384
1385 prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
1386 prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
1387 }
1388
1389 this->gbuffer_pass_sync(inst_);
1390
1392 closure_count_ = 0;
1393}
1394
1396{
1397 if (!prepass_ps_.is_empty()) {
1398 PassSimple &pass = eval_light_ps_;
1399 pass.init();
1401 pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_PLANAR_EVAL));
1402 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1403 pass.bind_resources(inst_.uniform_data);
1404 pass.bind_resources(inst_.gbuffer);
1405 pass.bind_resources(inst_.lights);
1406 pass.bind_resources(inst_.shadows);
1407 pass.bind_resources(inst_.sampling);
1408 pass.bind_resources(inst_.hiz_buffer.front);
1409 pass.bind_resources(inst_.sphere_probes);
1410 pass.bind_resources(inst_.volume_probes);
1412 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
1413 }
1414}
1415
1423
1425{
1426 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
1427 if (closure_bits == eClosureBits(0)) {
1428 /* Fix the case where there is no active closure in the shader.
1429 * In this case we force the evaluation of emission to avoid disabling the entire layer by
1430 * accident, see #126459. */
1431 closure_bits |= CLOSURE_EMISSION;
1432 }
1433 closure_bits_ |= closure_bits;
1435
1436 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
1437 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
1438
1439 PassMain::Sub *pass = (has_shader_to_rgba) ?
1440 ((backface_culling) ? gbuffer_single_sided_hybrid_ps_ :
1442 ((backface_culling) ? gbuffer_single_sided_ps_ :
1444
1445 return &pass->sub(GPU_material_get_name(gpumat));
1446}
1447
1449 GPUTexture *depth_layer_tx,
1450 Framebuffer &gbuffer_fb,
1451 Framebuffer &combined_fb,
1452 int2 extent)
1453{
1454 GPU_debug_group_begin("Planar.Capture");
1455
1456 inst_.pipelines.data.is_sphere_probe = true;
1457 inst_.uniform_data.push_update();
1458
1459 GPU_framebuffer_bind(gbuffer_fb);
1460 GPU_framebuffer_clear_depth(gbuffer_fb, inst_.film.depth.clear_value);
1461 inst_.manager->submit(prepass_ps_, view);
1462
1463 /* TODO(fclem): This is the only place where we use the layer source to HiZ.
1464 * This is because the texture layer view is still a layer texture. */
1465 inst_.hiz_buffer.set_source(&depth_layer_tx, 0);
1466 inst_.hiz_buffer.update();
1467
1468 inst_.lights.set_view(view, extent);
1469 inst_.shadows.set_view(view, extent);
1470 inst_.volume_probes.set_view(view);
1471 inst_.sphere_probes.set_view(view);
1472
1473 inst_.gbuffer.bind(gbuffer_fb);
1474 inst_.manager->submit(gbuffer_ps_, view);
1475
1476 GPU_framebuffer_bind(combined_fb);
1477 inst_.manager->submit(eval_light_ps_, view);
1478
1479 inst_.pipelines.data.is_sphere_probe = false;
1480 inst_.uniform_data.push_update();
1481
1483}
1484
1486
1487/* -------------------------------------------------------------------- */
1491
1493{
1494 surface_ps_.init();
1495 /* Surfel output is done using a SSBO, so no need for a fragment shader output color or depth. */
1496 /* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */
1497 surface_ps_.state_set(DRW_STATE_WRITE_STENCIL);
1498 surface_ps_.framebuffer_set(&inst_.volume_probes.bake.empty_raster_fb_);
1499
1500 surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.volume_probes.bake.surfels_buf_);
1501 surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.volume_probes.bake.capture_info_buf_);
1502
1503 surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1504 /* TODO(fclem): Remove. Bind to get the camera data,
1505 * but there should be no view dependent behavior during capture. */
1506 surface_ps_.bind_resources(inst_.uniform_data);
1507}
1508
1510{
1511 PassMain::Sub &sub_pass = surface_ps_.sub(GPU_material_get_name(gpumat));
1512 GPUPass *gpupass = GPU_material_get_pass(gpumat);
1513 sub_pass.shader_set(GPU_pass_shader_get(gpupass));
1514 sub_pass.push_constant("is_double_sided",
1516 return &sub_pass;
1517}
1518
1520{
1521 inst_.manager->submit(surface_ps_, view);
1522}
1523
1525
1526} // namespace blender::eevee
void BKE_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3])
std::optional< blender::Bounds< blender::float3 > > BKE_object_boundbox_get(const Object *ob)
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE float square_f(float a)
MINLINE int count_bits_i(unsigned int n)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
@ MA_VOLUME_ISECT_FAST
@ MA_BL_LIGHTPROBE_VOLUME_DOUBLE_SIDED
@ MA_BL_THICKNESS_FROM_SHADOW
@ MA_BL_CULL_BACKFACE
@ MA_BL_SS_REFRACTION
@ MA_BL_CULL_BACKFACE_SHADOW
@ MA_BL_HIDE_BACKFACE
@ OB_VOLUME
@ SCE_EEVEE_SSR_ENABLED
static AppView * view
bool GPU_stencil_export_support()
bool GPU_stencil_clasify_buffer_workaround()
@ GPU_ATTACHMENT_WRITE
@ GPU_ATTACHMENT_READ
@ GPU_ATTACHMENT_IGNORE
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_framebuffer_bind(GPUFrameBuffer *fb)
void GPU_framebuffer_clear_depth(GPUFrameBuffer *fb, float clear_depth)
GPUPass * GPU_material_get_pass(GPUMaterial *material)
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
@ GPU_MATFLAG_VOLUME_SCATTER
@ GPU_MATFLAG_VOLUME_ABSORPTION
@ GPU_MATFLAG_TRANSPARENT
@ GPU_MAT_SUCCESS
const char * GPU_material_get_name(GPUMaterial *material)
bool GPU_material_has_volume_output(GPUMaterial *mat)
GPUShader * GPU_pass_shader_get(GPUPass *pass)
Definition gpu_pass.cc:173
@ GPU_PRIM_TRIS
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
void GPU_texture_copy(GPUTexture *dst, GPUTexture *src)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
@ GPU_R16F
@ GPU_RGBA32F
@ GPU_RGBA16F
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
long long int int64_t
void submit(PassSimple &pass, View &view)
void clear(float4 values)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
void bind_resources(U &resources)
Definition draw_pass.hh:440
void draw_procedural_indirect(GPUPrimType primitive, StorageBuffer< DrawCommand, true > &indirect_buffer, ResourceHandle handle={0})
Definition draw_pass.hh:980
void bind_image(const char *name, GPUTexture *image)
void specialize_constant(GPUShader *shader, const char *name, const float &data)
void clear_stencil(uint8_t stencil)
void subpass_transition(GPUAttachmentState depth_attachment, Span< GPUAttachmentState > color_attachments)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:681
void submit(command::RecordingState &state) const
Definition draw_pass.hh:744
void barrier(eGPUBarrier type)
void state_set(DRWState state, int clip_plane_count=0)
void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask)
void material_set(Manager &manager, GPUMaterial *material, bool deferred_texture_loading=false)
void push_constant(const char *name, const float &data)
void draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first=-1, ResourceHandleRange handle={0}, uint custom_id=0)
Definition draw_pass.hh:953
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:490
void render(View &view, Framebuffer &combined_fb)
void sync(GPUMaterial *gpumat, float background_opacity, float background_blur)
const CameraData & data_get() const
bool is_perspective() const
PassMain::Sub * surface_material_add(::Material *blender_mat, GPUMaterial *gpumat)
GPUTexture * render(View &main_view, View &render_view, Framebuffer &prepass_fb, Framebuffer &combined_fb, Framebuffer &gbuffer_fb, int2 extent, RayTraceBuffer &rt_buffer, GPUTexture *radiance_behind_tx)
void end_sync(bool is_first_pass, bool is_last_pass, bool next_layer_has_transmission)
static bool do_merge_direct_indirect_eval(const Instance &inst)
static bool do_split_direct_indirect_radiance(const Instance &inst)
PassMain::Sub * prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion)
PassMain::Sub * material_add(::Material *blender_mat, GPUMaterial *gpumat)
PassMain::Sub * prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion)
void debug_draw(draw::View &view, GPUFrameBuffer *combined_fb)
PassMain::Sub * material_add(::Material *blender_mat, GPUMaterial *gpumat)
void render(View &main_view, View &render_view, Framebuffer &prepass_fb, Framebuffer &combined_fb, Framebuffer &gbuffer_fb, int2 extent, RayTraceBuffer &rt_buffer_opaque_layer, RayTraceBuffer &rt_buffer_refract_layer)
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, Framebuffer &gbuffer_fb, int2 extent)
PassMain::Sub * material_add(::Material *blender_mat, GPUMaterial *gpumat)
PassMain::Sub * prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
PassMain::Sub * prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion)
PassMain::Sub * material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
PassMain::Sub * material_transparent_add(const Object *ob, ::Material *blender_mat, GPUMaterial *gpumat)
void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent)
PassMain::Sub * prepass_transparent_add(const Object *ob, ::Material *blender_mat, GPUMaterial *gpumat)
struct blender::eevee::HiZBuffer::@117223074165324154125050272272211024005337204302 front
A running instance of the engine.
VolumeProbeModule volume_probes
SphereProbeModule sphere_probes
void info_append(const char *msg, Args &&...args)
UniformDataModule uniform_data
PassMain::Sub * prepass_add(::Material *blender_mat, GPUMaterial *gpumat)
void render(View &view, GPUTexture *depth_layer_tx, Framebuffer &gbuffer, Framebuffer &combined_fb, int2 extent)
PassMain::Sub * material_add(::Material *blender_mat, GPUMaterial *gpumat)
GPUShader * static_shader_get(eShaderType shader_type)
static ShadowTechnique shadow_technique
PassMain::Sub * surface_material_add(::Material *material, GPUMaterial *gpumat)
void add_object_bound(const VolumeObjectBounds &object_bounds)
void render(View &view, Texture &occupancy_tx)
bool bounds_overlaps(const VolumeObjectBounds &object_bounds) const
PassMain::Sub * occupancy_add(const Object *ob, const ::Material *blender_mat, GPUMaterial *gpumat)
PassMain::Sub * material_add(const Object *ob, const ::Material *blender_mat, GPUMaterial *gpumat)
std::optional< Bounds< float > > object_integration_range() const
VolumeLayer * register_and_get_layer(Object *ob)
void render(View &view, Texture &occupancy_tx)
void sync(GPUMaterial *gpumat)
DRWState
Definition draw_state.hh:25
@ DRW_STATE_STENCIL_EQUAL
Definition draw_state.hh:47
@ DRW_STATE_STENCIL_ALWAYS
Definition draw_state.hh:46
@ DRW_STATE_DEPTH_LESS
Definition draw_state.hh:37
@ DRW_STATE_DEPTH_EQUAL
Definition draw_state.hh:39
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_CLIP_CONTROL_UNIT_RANGE
Definition draw_state.hh:68
@ DRW_STATE_BLEND_ADD_FULL
Definition draw_state.hh:53
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_CULL_BACK
Definition draw_state.hh:43
@ DRW_STATE_STENCIL_NEQUAL
Definition draw_state.hh:48
@ DRW_STATE_DEPTH_ALWAYS
Definition draw_state.hh:36
@ DRW_STATE_BLEND_CUSTOM
Definition draw_state.hh:63
@ DRW_STATE_DEPTH_GREATER
Definition draw_state.hh:40
@ DRW_STATE_WRITE_STENCIL
Definition draw_state.hh:32
#define RBUFS_UTILITY_TEX_SLOT
#define CAPTURE_BUF_SLOT
#define RBUFS_CRYPTOMATTE_SLOT
#define SHADOW_ATLAS_IMG_SLOT
#define SHADOW_RENDER_MAP_BUF_SLOT
#define RBUFS_VALUE_SLOT
#define RBUFS_COLOR_SLOT
#define SHADOW_RENDER_VIEW_BUF_SLOT
#define SHADOW_PAGE_INFO_SLOT
#define CLIP_PLANE_BUF
#define DEFERRED_RADIANCE_FORMAT
#define SURFEL_BUF_SLOT
static ulong state[N]
Bounds< T > merge(const Bounds< T > &a, const Bounds< T > &b)
Definition BLI_bounds.hh:26
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
std::optional< Bounds< T > > intersect(const Bounds< T > &a, const Bounds< T > &b)
PassMain::Sub * volume_sub_pass(PassMain::Sub &ps, Scene *scene, Object *ob, GPUMaterial *gpu_material)
detail::Pass< command::DrawCommandBuf > PassSimple
detail::Pass< command::DrawMultiBuf > PassMain
static eClosureBits shader_closure_bits_from_flag(const GPUMaterial *gpumat)
T reduce_max(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
VectorT project_point(const MatT &mat, const VectorT &point)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
VecBase< int32_t, 4 > int4
VecBase< uint32_t, 4 > uint4
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
#define FLT_MAX
Definition stdcycles.h:14
float vec[8][3]
struct SceneEEVEE eevee
VecBase< T, 2 > xy() const
PassMain::Sub * gbuffer_double_sided_hybrid_ps_
PassMain::Sub * prepass_double_sided_static_ps_
PassMain::Sub * prepass_single_sided_static_ps_
void gbuffer_pass_sync(Instance &inst)
PassMain::Sub * prepass_double_sided_moving_ps_
PassMain::Sub * gbuffer_single_sided_hybrid_ps_
PassMain::Sub * prepass_single_sided_moving_ps_
void bind_optional_layers(PassType &pass)
GPUTexture * feedback_ensure(bool is_dummy, int2 extent)
std::optional< Bounds< float2 > > screen_bounds
VolumeObjectBounds(const Camera &camera, Object *ob)
std::optional< Bounds< float > > z_range
i
Definition text_draw.cc:230