Blender V5.0
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 clear_ps_.init();
40 clear_ps_.state_set(DRW_STATE_WRITE_COLOR);
41 clear_ps_.shader_set(inst_.shaders.static_shader_get(RENDERPASS_CLEAR));
42 /* RenderPasses & AOVs. Cleared by background (even if bad practice). */
43 clear_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx);
44 clear_ps_.bind_image("rp_value_img", &rbufs.rp_value_tx);
45 clear_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
46 /* Required by validation layers. */
47 clear_ps_.bind_resources(inst_.cryptomatte);
48 clear_ps_.bind_resources(inst_.uniform_data);
49 clear_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
50 /* To allow opaque pass rendering over it. */
51 clear_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
52
53 world_ps_.init();
56 world_ps_.material_set(manager, gpumat);
57 world_ps_.push_constant("world_opacity_fade", background_opacity);
58 world_ps_.push_constant("world_background_blur", square_f(background_blur));
59 SphereProbeData &world_data = *static_cast<SphereProbeData *>(&inst_.light_probes.world_sphere_);
60 world_ps_.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_data.atlas_coord));
61 world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx);
62 /* RenderPasses & AOVs. */
63 world_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx);
64 world_ps_.bind_image("rp_value_img", &rbufs.rp_value_tx);
65 world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
66 /* Required by validation layers. */
67 world_ps_.bind_resources(inst_.cryptomatte);
68 world_ps_.bind_resources(inst_.uniform_data);
69 world_ps_.bind_resources(inst_.sampling);
70 world_ps_.bind_resources(inst_.sphere_probes);
71 world_ps_.bind_resources(inst_.volume_probes);
72 world_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
73 /* To allow opaque pass rendering over it. */
74 world_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::TextureFormat::SFLOAT_32_32_32_32, extent, usage);
100 dummy_renderpass_tx_.ensure_2d(gpu::TextureFormat::SFLOAT_16_16_16_16, extent, usage);
101 dummy_aov_color_tx_.ensure_2d_array(gpu::TextureFormat::SFLOAT_16_16_16_16, extent, 1, usage);
102 dummy_aov_value_tx_.ensure_2d_array(gpu::TextureFormat::SFLOAT_16, 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
543 /* Clear stencil buffer so that prepass can tag it. Then draw a fullscreen triangle that will
544 * clear AOVs for all the pixels touched by this layer. */
545 prepass_ps_.clear_stencil(0xFFu);
546 prepass_ps_.state_stencil(0xFFu, 0u, 0xFFu);
547
550 inst_.film.depth.test_state;
551 DRWState state_depth_color = state_depth_only | DRW_STATE_WRITE_COLOR;
552
553 prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
554 prepass_double_sided_static_ps_->state_set(state_depth_only);
555
556 prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
557 prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
558
559 prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
560 prepass_double_sided_moving_ps_->state_set(state_depth_color);
561
562 prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
563 prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
564 }
565
566 this->gbuffer_pass_sync(inst_);
567}
568
573
579
580void DeferredLayer::end_sync(bool is_first_pass,
581 bool is_last_pass,
582 bool next_layer_has_transmission)
583{
584 const bool has_any_closure = closure_bits_ != 0;
585 /* We need the feedback output in case of refraction in the next pass (see #126455). */
586 const bool is_layer_refracted = (next_layer_has_transmission && has_any_closure);
587 const bool has_transmit_closure = (closure_bits_ & (CLOSURE_REFRACTION | CLOSURE_TRANSLUCENT));
588 const bool has_reflect_closure = (closure_bits_ & (CLOSURE_REFLECTION | CLOSURE_DIFFUSE));
589 use_raytracing_ = (has_transmit_closure || has_reflect_closure) &&
590 inst_.raytracing.use_raytracing();
591 use_clamp_direct_ = inst_.sampling.use_clamp_direct();
592 use_clamp_indirect_ = inst_.sampling.use_clamp_indirect();
593 /* Is the radiance split for the combined pass. */
594 use_split_radiance_ = use_raytracing_ || (use_clamp_direct_ || use_clamp_indirect_);
595
596 /* The first pass will never have any surfaces behind it. Nothing is refracted except the
597 * environment. So in this case, disable tracing and fallback to probe. */
598 use_screen_transmission_ = use_raytracing_ && has_transmit_closure && !is_first_pass;
599 use_screen_reflection_ = use_raytracing_ && has_reflect_closure;
600
601 use_feedback_output_ = (use_raytracing_ || is_layer_refracted) &&
602 (!is_last_pass || use_screen_reflection_);
603
604 /* Clear AOVs in case previous layers wrote to them. First pass always get clear buffer because
605 * of #BackgroundPipeline::clear(). */
606 if (inst_.film.aovs_info.color_len > 0 && !is_first_pass) {
607 gpu::Shader *sh = inst_.shaders.static_shader_get(DEFERRED_AOV_CLEAR);
608 PassMain::Sub &sub = prepass_ps_.sub("AOVsClear");
609 sub.shader_set(sh);
611 sub.bind_image("rp_color_img", &inst_.render_buffers.rp_color_tx);
612 sub.bind_image("rp_value_img", &inst_.render_buffers.rp_value_tx);
613 sub.bind_image("rp_cryptomatte_img", &inst_.render_buffers.cryptomatte_tx);
614 sub.bind_resources(inst_.cryptomatte);
615 sub.bind_resources(inst_.uniform_data);
616 sub.state_stencil(0xFFu, 0x0u, 0xFFu);
618 }
619
620 {
621 RenderBuffersInfoData &rbuf_data = inst_.render_buffers.data;
622
623 /* Add the stencil classification step at the end of the GBuffer pass. */
624 {
625 gpu::Shader *sh = inst_.shaders.static_shader_get(DEFERRED_TILE_CLASSIFY);
626 PassMain::Sub &sub = gbuffer_ps_.sub("StencilClassify");
627 sub.subpass_transition(GPU_ATTACHMENT_WRITE, /* Needed for depth test. */
629 GPU_ATTACHMENT_READ, /* Header. */
633 sub.shader_set(sh);
635 /* Binding any buffer to satisfy the binding. The buffer is not actually used. */
636 sub.bind_ssbo("dummy_workaround_buf", &inst_.film.aovs_info);
637 }
640 /* The shader sets the stencil directly in one full-screen pass. */
641 sub.state_stencil(uint8_t(StencilBits::HEADER_BITS), /* Set by shader */ 0x0u, 0xFFu);
643 }
644 else {
645 /* The shader cannot set the stencil directly. So we do one full-screen pass for each
646 * stencil bit we need to set and accumulate the result. */
647 auto set_bit = [&](StencilBits bit) {
648 sub.push_constant("current_bit", int(bit));
649 sub.state_stencil(uint8_t(bit), 0xFFu, 0xFFu);
651 };
652
653 if (closure_count_ > 0) {
654 set_bit(StencilBits::CLOSURE_COUNT_0);
655 }
656 if (closure_count_ > 1) {
657 set_bit(StencilBits::CLOSURE_COUNT_1);
658 }
660 set_bit(StencilBits::TRANSMISSION);
661 }
662 }
663 }
664
665 {
666 PassSimple &pass = eval_light_ps_;
667 pass.init();
668
669 /* TODO(fclem): Could also skip if no material uses thickness from shadow. */
671 PassSimple::Sub &sub = pass.sub("Eval.ThicknessFromShadow");
672 sub.shader_set(inst_.shaders.static_shader_get(DEFERRED_THICKNESS_AMEND));
673 sub.bind_resources(inst_.lights);
674 sub.bind_resources(inst_.shadows);
675 sub.bind_resources(inst_.hiz_buffer.front);
676 sub.bind_resources(inst_.uniform_data);
677 sub.bind_resources(inst_.sampling);
678 sub.bind_texture("gbuf_header_tx", &inst_.gbuffer.header_tx);
679 sub.bind_image("gbuf_normal_img", &inst_.gbuffer.normal_tx);
681 /* Render where there is transmission and the thickness from shadow bit is set. */
682 uint8_t stencil_bits = uint8_t(StencilBits::TRANSMISSION) |
683 uint8_t(StencilBits::THICKNESS_FROM_SHADOW);
684 sub.state_stencil(0x0u, stencil_bits, stencil_bits);
687 }
688 {
689 const bool use_transmission = (closure_bits_ & CLOSURE_TRANSMISSION) != 0;
690 const bool use_split_indirect = do_split_direct_indirect_radiance(inst_);
691 const bool use_lightprobe_eval = do_merge_direct_indirect_eval(inst_);
692 PassSimple::Sub &sub = pass.sub("Eval.Light");
693 /* Use depth test to reject background pixels which have not been stencil cleared. */
694 /* WORKAROUND: Avoid rasterizer discard by enabling stencil write, but the shaders actually
695 * use no fragment output. */
697 sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
698 sub.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
699 sub.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
700 const ShadowSceneData &shadow_scene = inst_.shadows.get_data();
701 auto set_specialization_constants =
702 [&](PassSimple::Sub &sub, gpu::Shader *sh, bool use_transmission) {
703 sub.specialize_constant(sh, "render_pass_shadow_id", rbuf_data.shadow_id);
704 sub.specialize_constant(sh, "use_split_indirect", use_split_indirect);
705 sub.specialize_constant(sh, "use_lightprobe_eval", use_lightprobe_eval);
706 sub.specialize_constant(sh, "use_transmission", use_transmission);
707 sub.specialize_constant(sh, "shadow_ray_count", &shadow_scene.ray_count);
708 sub.specialize_constant(sh, "shadow_ray_step_count", &shadow_scene.step_count);
709 };
710 /* Submit the more costly ones first to avoid long tail in occupancy.
711 * See page 78 of "SIGGRAPH 2023: Unreal Engine Substrate" by Hillaire & de Rousiers. */
712
713 for (int i = min_ii(3, closure_count_) - 1; i >= 0; i--) {
714 gpu::Shader *sh = inst_.shaders.static_shader_get(
716 set_specialization_constants(sub, sh, false);
717 sub.shader_set(sh);
718 sub.bind_image("direct_radiance_1_img", &direct_radiance_txs_[0]);
719 sub.bind_image("direct_radiance_2_img", &direct_radiance_txs_[1]);
720 sub.bind_image("direct_radiance_3_img", &direct_radiance_txs_[2]);
721 sub.bind_image("indirect_radiance_1_img", &indirect_result_.closures[0]);
722 sub.bind_image("indirect_radiance_2_img", &indirect_result_.closures[1]);
723 sub.bind_image("indirect_radiance_3_img", &indirect_result_.closures[2]);
724 sub.bind_resources(inst_.uniform_data);
725 sub.bind_resources(inst_.gbuffer);
726 sub.bind_resources(inst_.lights);
727 sub.bind_resources(inst_.shadows);
728 sub.bind_resources(inst_.sampling);
729 sub.bind_resources(inst_.hiz_buffer.front);
730 sub.bind_resources(inst_.sphere_probes);
731 sub.bind_resources(inst_.volume_probes);
732 uint8_t compare_mask = uint8_t(StencilBits::CLOSURE_COUNT_0) |
733 uint8_t(StencilBits::CLOSURE_COUNT_1) |
734 uint8_t(StencilBits::TRANSMISSION);
735 sub.state_stencil(0x0u, i + 1, compare_mask);
737 if (use_transmission) {
738 /* Separate pass for transmission BSDF as their evaluation is quite costly. */
739 set_specialization_constants(sub, sh, true);
740 sub.shader_set(sh);
741 sub.state_stencil(0x0u, (i + 1) | uint8_t(StencilBits::TRANSMISSION), compare_mask);
743 }
744 }
745 }
746 }
747 {
748 PassSimple &pass = combine_ps_;
749 pass.init();
750 gpu::Shader *sh = inst_.shaders.static_shader_get(DEFERRED_COMBINE);
751 /* TODO(fclem): Could specialize directly with the pass index but this would break it for
752 * OpenGL and Vulkan implementation which aren't fully supporting the specialize
753 * constant. */
754 pass.specialize_constant(sh,
755 "render_pass_diffuse_light_enabled",
756 (rbuf_data.diffuse_light_id != -1) ||
757 (rbuf_data.diffuse_color_id != -1));
758 pass.specialize_constant(sh,
759 "render_pass_specular_light_enabled",
760 (rbuf_data.specular_light_id != -1) ||
761 (rbuf_data.specular_color_id != -1));
762 pass.specialize_constant(sh, "use_split_radiance", use_split_radiance_);
764 sh, "use_radiance_feedback", use_feedback_output_ && use_clamp_direct_);
765 pass.specialize_constant(sh, "render_pass_normal_enabled", rbuf_data.normal_id != -1);
766 pass.specialize_constant(sh, "render_pass_position_enabled", rbuf_data.position_id != -1);
767 pass.shader_set(sh);
768 /* Use stencil test to reject pixels not written by this layer. */
770 /* Render where stencil is not 0. */
771 pass.state_stencil(0x0u, 0x0u, uint8_t(StencilBits::HEADER_BITS));
772 pass.bind_texture("direct_radiance_1_tx", &direct_radiance_txs_[0]);
773 pass.bind_texture("direct_radiance_2_tx", &direct_radiance_txs_[1]);
774 pass.bind_texture("direct_radiance_3_tx", &direct_radiance_txs_[2]);
775 pass.bind_texture("indirect_radiance_1_tx", &indirect_result_.closures[0]);
776 pass.bind_texture("indirect_radiance_2_tx", &indirect_result_.closures[1]);
777 pass.bind_texture("indirect_radiance_3_tx", &indirect_result_.closures[2]);
778 pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
779 pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
780 pass.bind_image("radiance_feedback_img", &radiance_feedback_tx_);
781 pass.bind_resources(inst_.gbuffer);
782 pass.bind_resources(inst_.uniform_data);
783 pass.bind_resources(inst_.hiz_buffer.front);
785 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
786 }
787 }
788}
789
791 GPUMaterial *gpumat,
792 bool has_motion)
793{
794 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
795 (has_motion ? prepass_single_sided_moving_ps_ :
797 (has_motion ? prepass_double_sided_moving_ps_ :
799
800 return &pass->sub(GPU_material_get_name(gpumat));
801}
802
804{
805 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
806 if (closure_bits == eClosureBits(0)) {
807 /* Fix the case where there is no active closure in the shader.
808 * In this case we force the evaluation of emission to avoid disabling the entire layer by
809 * accident, see #126459. */
810 closure_bits |= CLOSURE_EMISSION;
811 }
812 closure_bits_ |= closure_bits;
814
815 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
816 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
817 bool use_thickness_from_shadow = (blender_mat->blend_flag & MA_BL_THICKNESS_FROM_SHADOW) != 0;
818
819 PassMain::Sub *pass = (has_shader_to_rgba) ?
820 ((backface_culling) ? gbuffer_single_sided_hybrid_ps_ :
822 ((backface_culling) ? gbuffer_single_sided_ps_ :
824
825 PassMain::Sub *material_pass = &pass->sub(GPU_material_get_name(gpumat));
826 /* Set stencil for some deferred specialized shaders. */
827 uint8_t material_stencil_bits = 0u;
828 if (use_thickness_from_shadow) {
829 material_stencil_bits |= uint8_t(StencilBits::THICKNESS_FROM_SHADOW);
830 }
831 /* We use this opportunity to clear the stencil bits. The undefined areas are discarded using the
832 * gbuf header value. */
833 material_pass->state_stencil(0xFFu, material_stencil_bits, 0xFFu);
834
835 return material_pass;
836}
837
839 View &render_view,
840 Framebuffer &prepass_fb,
841 Framebuffer &combined_fb,
842 Framebuffer &gbuffer_fb,
843 int2 extent,
844 RayTraceBuffer &rt_buffer,
845 gpu::Texture *radiance_behind_tx)
846{
847 if (closure_count_ == 0) {
848 return nullptr;
849 }
850
851 RenderBuffers &rb = inst_.render_buffers;
852
853 constexpr eGPUTextureUsage usage_read = GPU_TEXTURE_USAGE_SHADER_READ;
854 constexpr eGPUTextureUsage usage_write = GPU_TEXTURE_USAGE_SHADER_WRITE;
855 constexpr eGPUTextureUsage usage_rw = usage_read | usage_write;
856
857 if (use_screen_transmission_) {
858 /* Update for refraction. */
859 inst_.hiz_buffer.update();
860 }
861
862 GPU_framebuffer_bind(prepass_fb);
863 inst_.manager->submit(prepass_ps_, render_view);
864
865 inst_.hiz_buffer.swap_layer();
866 /* Update for lighting pass or AO node. */
867 inst_.hiz_buffer.update();
868
869 inst_.volume_probes.set_view(render_view);
870 inst_.sphere_probes.set_view(render_view);
871 inst_.shadows.set_view(render_view, extent);
872
873 inst_.gbuffer.bind(gbuffer_fb);
874 inst_.manager->submit(gbuffer_ps_, render_view);
875
876 for (int i = 0; i < ARRAY_SIZE(direct_radiance_txs_); i++) {
877 direct_radiance_txs_[i].acquire((closure_count_ > i) ? extent : int2(1),
878 gpu::TextureFormat::DEFERRED_RADIANCE_FORMAT,
879 usage_rw);
880 }
881
882 if (use_raytracing_) {
883 indirect_result_ = inst_.raytracing.render(
884 rt_buffer, radiance_behind_tx, closure_bits_, main_view, render_view);
885 }
886 else if (use_split_radiance_) {
887 indirect_result_ = inst_.raytracing.alloc_only(rt_buffer);
888 }
889 else {
890 indirect_result_ = inst_.raytracing.alloc_dummy(rt_buffer);
891 }
892
893 GPU_framebuffer_bind(combined_fb);
894 inst_.manager->submit(eval_light_ps_, render_view);
895
896 inst_.subsurface.render(
897 direct_radiance_txs_[0], indirect_result_.closures[0], closure_bits_, render_view);
898
899 radiance_feedback_tx_ = rt_buffer.feedback_ensure(!use_feedback_output_, extent);
900
901 if (use_feedback_output_ && use_clamp_direct_) {
902 /* We need to do a copy before the combine pass (otherwise we have a dependency issue) to save
903 * the emission and the previous layer's radiance. */
904 GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx);
905 }
906
907 GPU_framebuffer_bind(combined_fb);
908 inst_.manager->submit(combine_ps_, render_view);
909
910 if (use_feedback_output_ && !use_clamp_direct_) {
911 /* We skip writing the radiance during the combine pass. Do a simple fast copy. */
912 GPU_texture_copy(radiance_feedback_tx_, rb.combined_tx);
913 }
914
915 indirect_result_.release();
916
917 for (int i = 0; i < ARRAY_SIZE(direct_radiance_txs_); i++) {
918 direct_radiance_txs_[i].release();
919 }
920
921 inst_.pipelines.deferred.debug_draw(render_view, combined_fb);
922
923 return use_feedback_output_ ? radiance_feedback_tx_ : nullptr;
924}
925
927
928/* -------------------------------------------------------------------- */
933
935{
936 Instance &inst = opaque_layer_.inst_;
937
938 const bool use_raytracing = (inst.scene->eevee.flag & SCE_EEVEE_SSR_ENABLED) != 0;
939 use_combined_lightprobe_eval = !use_raytracing;
940
941 opaque_layer_.begin_sync();
942 refraction_layer_.begin_sync();
943}
944
946{
947 Instance &inst = opaque_layer_.inst_;
948
949 opaque_layer_.end_sync(true, refraction_layer_.is_empty(), refraction_layer_.has_transmission());
950 refraction_layer_.end_sync(opaque_layer_.is_empty(), true, false);
951
953
954 debug_pass_sync();
955}
956
957void DeferredPipeline::debug_pass_sync()
958{
959 Instance &inst = opaque_layer_.inst_;
960 if (!ELEM(inst.debug_mode,
963 {
964 return;
965 }
966
967 PassSimple &pass = debug_draw_ps_;
968 pass.init();
970 pass.shader_set(inst.shaders.static_shader_get(DEBUG_GBUFFER));
971 pass.push_constant("debug_mode", int(inst.debug_mode));
972 pass.bind_resources(inst.gbuffer);
973 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
974}
975
977{
978 Instance &inst = opaque_layer_.inst_;
979 if (!ELEM(inst.debug_mode,
982 {
983 return;
984 }
985
986 switch (inst.debug_mode) {
988 inst.info_append("Debug Mode: Deferred Lighting Cost");
989 break;
991 inst.info_append("Debug Mode: Gbuffer Storage Cost");
992 break;
993 default:
994 /* Nothing to display. */
995 return;
996 }
997
998 GPU_framebuffer_bind(combined_fb);
999 inst.manager->submit(debug_draw_ps_, view);
1000}
1001
1003 GPUMaterial *gpumat,
1004 bool has_motion)
1005{
1006 if (!use_combined_lightprobe_eval && (blender_mat->blend_flag & MA_BL_SS_REFRACTION)) {
1007 return refraction_layer_.prepass_add(blender_mat, gpumat, has_motion);
1008 }
1009 return opaque_layer_.prepass_add(blender_mat, gpumat, has_motion);
1010}
1011
1013{
1014 if (!use_combined_lightprobe_eval && (blender_mat->blend_flag & MA_BL_SS_REFRACTION)) {
1015 return refraction_layer_.material_add(blender_mat, gpumat);
1016 }
1017 return opaque_layer_.material_add(blender_mat, gpumat);
1018}
1019
1021 View &render_view,
1022 Framebuffer &prepass_fb,
1023 Framebuffer &combined_fb,
1024 Framebuffer &gbuffer_fb,
1025 int2 extent,
1026 RayTraceBuffer &rt_buffer_opaque_layer,
1027 RayTraceBuffer &rt_buffer_refract_layer)
1028{
1029 gpu::Texture *feedback_tx = nullptr;
1030
1031 GPU_debug_group_begin("Deferred.Opaque");
1032 feedback_tx = opaque_layer_.render(main_view,
1033 render_view,
1034 prepass_fb,
1035 combined_fb,
1036 gbuffer_fb,
1037 extent,
1038 rt_buffer_opaque_layer,
1039 feedback_tx);
1041
1042 GPU_debug_group_begin("Deferred.Refract");
1043 feedback_tx = refraction_layer_.render(main_view,
1044 render_view,
1045 prepass_fb,
1046 combined_fb,
1047 gbuffer_fb,
1048 extent,
1049 rt_buffer_refract_layer,
1050 feedback_tx);
1052}
1053
1055
1056/* -------------------------------------------------------------------- */
1060
1062{
1063 object_bounds_.clear();
1064 combined_screen_bounds_ = std::nullopt;
1065 use_hit_list = false;
1066 is_empty = true;
1067 finalized = false;
1068 has_scatter = false;
1069 has_absorption = false;
1070
1071 draw::PassMain &layer_pass = volume_layer_ps_;
1072 layer_pass.init();
1073 layer_pass.clear_stencil(0x0u);
1074 {
1075 PassMain::Sub &pass = layer_pass.sub("occupancy_ps");
1076 /* Always double sided to let all fragments be invoked. */
1078 pass.bind_resources(inst_.uniform_data);
1079 pass.bind_resources(inst_.volume.occupancy);
1080 pass.bind_resources(inst_.sampling);
1081 occupancy_ps_ = &pass;
1082 }
1083 {
1084 PassMain::Sub &pass = layer_pass.sub("material_ps");
1085 /* Double sided with stencil equal to ensure only one fragment is invoked per pixel. */
1087 pass.state_stencil(0x1u, 0x1u, 0x1u);
1089 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1090 pass.bind_resources(inst_.uniform_data);
1091 pass.bind_resources(inst_.volume.properties);
1092 pass.bind_resources(inst_.sampling);
1093 material_ps_ = &pass;
1094 }
1095}
1096
1098 const ::Material *blender_mat,
1099 GPUMaterial *gpumat)
1100{
1102 "Only volume material should be added here");
1103 bool use_fast_occupancy = (ob->type == OB_VOLUME) ||
1104 (blender_mat->volume_intersection_method == MA_VOLUME_ISECT_FAST);
1105 use_hit_list |= !use_fast_occupancy;
1106 is_empty = false;
1107
1108 PassMain::Sub *pass = &occupancy_ps_->sub(GPU_material_get_name(gpumat));
1109 pass->material_set(*inst_.manager, gpumat, true);
1110 pass->push_constant("use_fast_method", use_fast_occupancy);
1111 return pass;
1112}
1113
1115 const ::Material * /*blender_mat*/,
1116 GPUMaterial *gpumat)
1117{
1119 "Only volume material should be added here");
1121
1122 PassMain::Sub *pass = &material_ps_->sub(GPU_material_get_name(gpumat));
1123 pass->material_set(*inst_.manager, gpumat, true);
1125 has_scatter = true;
1126 }
1128 has_absorption = true;
1129 }
1130 return pass;
1131}
1132
1133bool VolumeLayer::bounds_overlaps(const VolumeObjectBounds &object_bounds) const
1134{
1135 /* First check the biggest area. */
1136 if (bounds::intersect(object_bounds.screen_bounds, combined_screen_bounds_)) {
1137 return true;
1138 }
1139 /* Check against individual bounds to try to squeeze the new object between them. */
1140 for (const std::optional<Bounds<float2>> &other_aabb : object_bounds_) {
1141 if (bounds::intersect(object_bounds.screen_bounds, other_aabb)) {
1142 return true;
1143 }
1144 }
1145 return false;
1146}
1147
1149{
1150 object_bounds_.append(object_bounds.screen_bounds);
1151 combined_screen_bounds_ = bounds::merge(combined_screen_bounds_, object_bounds.screen_bounds);
1152}
1153
1155{
1156 if (is_empty) {
1157 return;
1158 }
1159 if (finalized == false) {
1160 finalized = true;
1161 if (use_hit_list) {
1162 /* Add resolve pass only when needed. Insert after occupancy, before material pass. */
1163 occupancy_ps_->shader_set(inst_.shaders.static_shader_get(VOLUME_OCCUPANCY_CONVERT));
1164 occupancy_ps_->barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
1165 occupancy_ps_->draw_procedural(GPU_PRIM_TRIS, 1, 3);
1166 }
1167 }
1168 /* TODO(fclem): Move this clear inside the render pass. */
1169 occupancy_tx.clear(uint4(0u));
1170 inst_.manager->submit(volume_layer_ps_, view);
1171}
1172
1174
1175/* -------------------------------------------------------------------- */
1178
1180{
1181 object_integration_range_ = std::nullopt;
1182 has_scatter_ = false;
1183 has_absorption_ = false;
1184 for (auto &layer : layers_) {
1185 layer->sync();
1186 }
1187}
1188
1190{
1191 for (auto &layer : layers_) {
1192 layer->render(view, occupancy_tx);
1193 }
1194}
1195
1197{
1198 /* TODO(fclem): For panoramic camera, we will have to do this check for each cube-face. */
1199 const float4x4 &view_matrix = camera.data_get().viewmat;
1200 /* Note in practice we only care about the projection type since we only care about 2D overlap,
1201 * and this is independent of FOV. */
1202 const float4x4 &projection_matrix = camera.data_get().winmat;
1203
1204 const Bounds<float3> bounds = BKE_object_boundbox_get(ob).value_or(Bounds(float3(0.0f)));
1205
1206 const std::array<float3, 8> corners = bounds::corners(bounds);
1207
1208 screen_bounds = std::nullopt;
1209 z_range = std::nullopt;
1210
1211 for (const float3 &l_corner : corners) {
1212 float3 ws_corner = math::transform_point(ob->object_to_world(), l_corner);
1213 /* Split view and projection for precision. */
1214 float3 vs_corner = math::transform_point(view_matrix, ws_corner);
1215 float3 ss_corner = math::project_point(projection_matrix, vs_corner);
1216
1217 z_range = bounds::min_max(z_range, vs_corner.z);
1218 if (camera.is_perspective() && vs_corner.z >= 1.0e-8f) {
1219 /* If the object is crossing the z=0 plane, we can't determine its 2D bounds easily.
1220 * In this case, consider the object covering the whole screen.
1221 * Still continue the loop for the Z range. */
1222 screen_bounds = Bounds<float2>(float2(-1.0f), float2(1.0f));
1223 }
1224 else {
1226 }
1227 }
1228}
1229
1231{
1232 /* TODO(fclem): This is against design. Sync shouldn't depend on view properties (camera). */
1233 VolumeObjectBounds object_bounds(inst_.camera, ob);
1234 if (math::reduce_max(object_bounds.screen_bounds->size()) < 1e-5) {
1235 /* WORKAROUND(fclem): Fixes an issue with 0 scaled object (see #132889).
1236 * Is likely to be an issue somewhere else in the pipeline but it is hard to find. */
1237 return nullptr;
1238 }
1239
1240 object_integration_range_ = bounds::merge(object_integration_range_, object_bounds.z_range);
1241
1242 /* Do linear search in all layers in order. This can be optimized. */
1243 for (auto &layer : layers_) {
1244 if (!layer->bounds_overlaps(object_bounds)) {
1245 layer->add_object_bound(object_bounds);
1246 return layer.get();
1247 }
1248 }
1249 /* No non-overlapping layer found. Create new one. */
1250 int64_t index = layers_.append_and_get_index(std::make_unique<VolumeLayer>(inst_));
1251 (*layers_[index]).add_object_bound(object_bounds);
1252 return layers_[index].get();
1253}
1254
1255std::optional<Bounds<float>> VolumePipeline::object_integration_range() const
1256{
1257 return object_integration_range_;
1258}
1259
1261{
1262 for (const auto &layer : layers_) {
1263 if (layer->use_hit_list) {
1264 return true;
1265 }
1266 }
1267 return false;
1268}
1269
1271
1272/* -------------------------------------------------------------------- */
1277
1279{
1280 draw::PassMain &pass = opaque_layer_.prepass_ps_;
1281 pass.init();
1282 {
1283 /* Common resources. */
1284
1285 /* Textures. */
1286 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1287
1288 pass.bind_resources(inst_.uniform_data);
1289 pass.bind_resources(inst_.velocity);
1290 pass.bind_resources(inst_.sampling);
1291 }
1292
1294 inst_.film.depth.test_state;
1295 /* Only setting up static pass because we don't use motion vectors for light-probes. */
1296 opaque_layer_.prepass_double_sided_static_ps_ = &pass.sub("DoubleSided");
1297 opaque_layer_.prepass_double_sided_static_ps_->state_set(state_depth_only);
1298 opaque_layer_.prepass_single_sided_static_ps_ = &pass.sub("SingleSided");
1299 opaque_layer_.prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
1300
1301 opaque_layer_.gbuffer_pass_sync(inst_);
1302}
1303
1305{
1306 if (!opaque_layer_.prepass_ps_.is_empty()) {
1307 PassSimple &pass = eval_light_ps_;
1308 pass.init();
1309 /* Use depth test to reject background pixels. */
1311 pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_CAPTURE_EVAL));
1312 pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
1313 pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
1314 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1315 pass.bind_resources(inst_.uniform_data);
1316 pass.bind_resources(inst_.gbuffer);
1317 pass.bind_resources(inst_.lights);
1318 pass.bind_resources(inst_.shadows);
1319 pass.bind_resources(inst_.sampling);
1320 pass.bind_resources(inst_.hiz_buffer.front);
1321 pass.bind_resources(inst_.volume_probes);
1323 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
1324 }
1325}
1326
1328{
1329 PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
1330 opaque_layer_.prepass_single_sided_static_ps_ :
1331 opaque_layer_.prepass_double_sided_static_ps_;
1332
1333 return &pass->sub(GPU_material_get_name(gpumat));
1334}
1335
1337{
1338 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
1339 if (closure_bits == eClosureBits(0)) {
1340 /* Fix the case where there is no active closure in the shader.
1341 * In this case we force the evaluation of emission to avoid disabling the entire layer by
1342 * accident, see #126459. */
1343 closure_bits |= CLOSURE_EMISSION;
1344 }
1345 opaque_layer_.closure_bits_ |= closure_bits;
1346 opaque_layer_.closure_count_ = max_ii(opaque_layer_.closure_count_, count_bits_i(closure_bits));
1347
1348 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
1349 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
1350
1351 PassMain::Sub *pass = (has_shader_to_rgba) ?
1352 ((backface_culling) ? opaque_layer_.gbuffer_single_sided_hybrid_ps_ :
1353 opaque_layer_.gbuffer_double_sided_hybrid_ps_) :
1354 ((backface_culling) ? opaque_layer_.gbuffer_single_sided_ps_ :
1355 opaque_layer_.gbuffer_double_sided_ps_);
1356
1357 return &pass->sub(GPU_material_get_name(gpumat));
1358}
1359
1361 Framebuffer &prepass_fb,
1362 Framebuffer &combined_fb,
1363 Framebuffer &gbuffer_fb,
1364 int2 extent)
1365{
1366 GPU_debug_group_begin("Probe.Render");
1367
1368 GPU_framebuffer_bind(prepass_fb);
1369 inst_.manager->submit(opaque_layer_.prepass_ps_, view);
1370
1371 inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
1372 inst_.hiz_buffer.update();
1373
1374 inst_.lights.set_view(view, extent);
1375 inst_.shadows.set_view(view, extent);
1376 inst_.volume_probes.set_view(view);
1377 inst_.sphere_probes.set_view(view);
1378
1379 /* Update for lighting pass. */
1380 inst_.hiz_buffer.update();
1381
1382 inst_.gbuffer.bind(gbuffer_fb);
1383 inst_.manager->submit(opaque_layer_.gbuffer_ps_, view);
1384
1385 GPU_framebuffer_bind(combined_fb);
1386 inst_.manager->submit(eval_light_ps_, view);
1387
1389}
1390
1392
1393/* -------------------------------------------------------------------- */
1397
1399{
1400 {
1401 prepass_ps_.init();
1402 prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1403 prepass_ps_.bind_ubo(CLIP_PLANE_BUF, inst_.planar_probes.world_clip_buf_);
1404 prepass_ps_.bind_resources(inst_.uniform_data);
1405 prepass_ps_.bind_resources(inst_.sampling);
1406
1408 inst_.film.depth.test_state;
1409
1410 prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
1411 prepass_double_sided_static_ps_->state_set(state_depth_only);
1412
1413 prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
1414 prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
1415 }
1416
1417 this->gbuffer_pass_sync(inst_);
1418
1420 closure_count_ = 0;
1421}
1422
1424{
1425 if (!prepass_ps_.is_empty()) {
1426 PassSimple &pass = eval_light_ps_;
1427 pass.init();
1429 pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_PLANAR_EVAL));
1430 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1431 pass.bind_resources(inst_.uniform_data);
1432 pass.bind_resources(inst_.gbuffer);
1433 pass.bind_resources(inst_.lights);
1434 pass.bind_resources(inst_.shadows);
1435 pass.bind_resources(inst_.sampling);
1436 pass.bind_resources(inst_.hiz_buffer.front);
1437 pass.bind_resources(inst_.sphere_probes);
1438 pass.bind_resources(inst_.volume_probes);
1440 pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
1441 }
1442}
1443
1451
1453{
1454 eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
1455 if (closure_bits == eClosureBits(0)) {
1456 /* Fix the case where there is no active closure in the shader.
1457 * In this case we force the evaluation of emission to avoid disabling the entire layer by
1458 * accident, see #126459. */
1459 closure_bits |= CLOSURE_EMISSION;
1460 }
1461 closure_bits_ |= closure_bits;
1463
1464 bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
1465 bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
1466
1467 PassMain::Sub *pass = (has_shader_to_rgba) ?
1468 ((backface_culling) ? gbuffer_single_sided_hybrid_ps_ :
1470 ((backface_culling) ? gbuffer_single_sided_ps_ :
1472
1473 return &pass->sub(GPU_material_get_name(gpumat));
1474}
1475
1477 gpu::Texture *depth_layer_tx,
1478 Framebuffer &gbuffer_fb,
1479 Framebuffer &combined_fb,
1480 int2 extent)
1481{
1482 GPU_debug_group_begin("Planar.Capture");
1483
1484 inst_.pipelines.data.is_sphere_probe = true;
1485 inst_.uniform_data.push_update();
1486
1487 GPU_framebuffer_bind(gbuffer_fb);
1488 GPU_framebuffer_clear_depth(gbuffer_fb, inst_.film.depth.clear_value);
1489 inst_.manager->submit(prepass_ps_, view);
1490
1491 /* TODO(fclem): This is the only place where we use the layer source to HiZ.
1492 * This is because the texture layer view is still a layer texture. */
1493 inst_.hiz_buffer.set_source(&depth_layer_tx, 0);
1494 inst_.hiz_buffer.update();
1495
1496 inst_.lights.set_view(view, extent);
1497 inst_.shadows.set_view(view, extent);
1498 inst_.volume_probes.set_view(view);
1499 inst_.sphere_probes.set_view(view);
1500
1501 inst_.gbuffer.bind(gbuffer_fb);
1502 inst_.manager->submit(gbuffer_ps_, view);
1503
1504 GPU_framebuffer_bind(combined_fb);
1505 inst_.manager->submit(eval_light_ps_, view);
1506
1507 inst_.pipelines.data.is_sphere_probe = false;
1508 inst_.uniform_data.push_update();
1509
1511}
1512
1514
1515/* -------------------------------------------------------------------- */
1519
1521{
1522 surface_ps_.init();
1523 /* Surfel output is done using a SSBO, so no need for a fragment shader output color or depth. */
1524 /* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */
1525 surface_ps_.state_set(DRW_STATE_WRITE_STENCIL);
1526 surface_ps_.framebuffer_set(&inst_.volume_probes.bake.empty_raster_fb_);
1527
1528 surface_ps_.bind_ssbo(SURFEL_BUF_SLOT, &inst_.volume_probes.bake.surfels_buf_);
1529 surface_ps_.bind_ssbo(CAPTURE_BUF_SLOT, &inst_.volume_probes.bake.capture_info_buf_);
1530
1531 surface_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
1532 /* TODO(fclem): Remove. Bind to get the camera data,
1533 * but there should be no view dependent behavior during capture. */
1534 surface_ps_.bind_resources(inst_.uniform_data);
1535}
1536
1538{
1539 PassMain::Sub &sub_pass = surface_ps_.sub(GPU_material_get_name(gpumat));
1540 GPUPass *gpupass = GPU_material_get_pass(gpumat);
1541 sub_pass.shader_set(GPU_pass_shader_get(gpupass));
1542 sub_pass.push_constant("is_double_sided",
1544 return &sub_pass;
1545}
1546
1548{
1549 inst_.manager->submit(surface_ps_, view);
1550}
1551
1553
1554} // namespace blender::eevee
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_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
@ MA_VOLUME_ISECT_FAST
@ 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_clear_depth(blender::gpu::FrameBuffer *fb, float clear_depth)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
GPUPass * GPU_material_get_pass(GPUMaterial *material)
@ GPU_MAT_SUCCESS
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
GPUMaterialStatus GPU_material_status(GPUMaterial *mat)
@ GPU_MATFLAG_VOLUME_SCATTER
@ GPU_MATFLAG_VOLUME_ABSORPTION
@ GPU_MATFLAG_TRANSPARENT
const char * GPU_material_get_name(GPUMaterial *material)
bool GPU_material_has_volume_output(GPUMaterial *mat)
blender::gpu::Shader * GPU_pass_shader_get(GPUPass *pass)
Definition gpu_pass.cc:175
@ 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(blender::gpu::Texture *dst, blender::gpu::Texture *src)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
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_resources(U &resources)
Definition draw_pass.hh:449
void shader_set(gpu::Shader *shader)
void bind_texture(const char *name, gpu::Texture *texture, GPUSamplerState state=sampler_auto)
void clear_stencil(uint8_t stencil)
void specialize_constant(gpu::Shader *shader, const char *name, const float &data)
void subpass_transition(GPUAttachmentState depth_attachment, Span< GPUAttachmentState > color_attachments)
void bind_image(const char *name, gpu::Texture *image)
void draw_procedural(GPUPrimType primitive, uint instance_len, uint vertex_len, uint vertex_first=-1, ResourceIndexRange res_index={}, uint custom_id=0)
Definition draw_pass.hh:964
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:690
void submit(command::RecordingState &state) const
Definition draw_pass.hh:753
void draw_procedural_indirect(GPUPrimType primitive, StorageBuffer< DrawCommand, true > &indirect_buffer, ResourceIndex res_index={0})
Definition draw_pass.hh:995
void state_set(DRWState state, int clip_plane_count=0)
void barrier(GPUBarrier type)
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 bind_ssbo(const char *name, gpu::StorageBuf *buffer)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
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)
gpu::Texture * render(View &main_view, View &render_view, Framebuffer &prepass_fb, Framebuffer &combined_fb, Framebuffer &gbuffer_fb, int2 extent, RayTraceBuffer &rt_buffer, gpu::Texture *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, gpu::FrameBuffer *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::@312272073133116306117315271010160007344264367302 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, gpu::Texture *depth_layer_tx, Framebuffer &gbuffer, Framebuffer &combined_fb, int2 extent)
PassMain::Sub * material_add(::Material *blender_mat, GPUMaterial *gpumat)
gpu::Shader * 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 SURFEL_BUF_SLOT
static ulong state[N]
Bounds< T > merge(const Bounds< T > &a, const Bounds< T > &b)
Definition BLI_bounds.hh:26
std::array< VecBase< T, 3 >, 8 > corners(const Bounds< VecBase< T, 3 > > &bounds)
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
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)
gpu::Texture * 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