From fe848ce3ef345cd8b3774c655877e77260a510f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 1 Dec 2023 14:41:13 +0100 Subject: [PATCH] EEVEE-Next: Optimize GBuffer Layout and writting This layout is more flexible and polymorphic. While the worst case is worse (4 + 3 layers), the common case is more optimized (2 + 2 layers). The average written closure data is also lower since we can compact the data for special cases which are quite frequent. Some adjustment had to be made in the denoise an tile classify shaders. Pull Request: https://projects.blender.org/blender/blender/pulls/115541 --- source/blender/draw/CMakeLists.txt | 1 + .../draw/engines/eevee_next/eevee_gbuffer.hh | 160 +++++++--- .../draw/engines/eevee_next/eevee_pipeline.cc | 168 +++++++---- .../draw/engines/eevee_next/eevee_pipeline.hh | 76 ++++- .../engines/eevee_next/eevee_planar_probes.cc | 14 +- .../engines/eevee_next/eevee_planar_probes.hh | 1 + .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 1 + .../engines/eevee_next/eevee_shader_shared.hh | 28 +- .../draw/engines/eevee_next/eevee_view.cc | 48 ++- .../draw/engines/eevee_next/eevee_view.hh | 26 +- .../shaders/eevee_debug_gbuffer_frag.glsl | 46 +++ .../shaders/eevee_deferred_capture_frag.glsl | 4 + .../shaders/eevee_deferred_combine_frag.glsl | 7 + .../shaders/eevee_deferred_light_frag.glsl | 12 +- .../eevee_next/shaders/eevee_gbuffer_lib.glsl | 284 +++++++++++++++--- .../shaders/eevee_horizon_denoise_comp.glsl | 22 +- .../eevee_ray_denoise_spatial_comp.glsl | 4 +- .../shaders/eevee_ray_tile_classify_comp.glsl | 6 +- .../shaders/eevee_surf_deferred_frag.glsl | 70 ++--- .../shaders/infos/eevee_deferred_info.hh | 14 + .../shaders/infos/eevee_material_info.hh | 4 +- source/blender/draw/intern/DRW_gpu_wrapper.hh | 33 +- .../gpu_shader_debug_gradients_lib.glsl | 4 + 24 files changed, 785 insertions(+), 250 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_debug_gbuffer_frag.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 194749aabfb..32259e596a8 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -468,6 +468,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_colorspace_lib.glsl engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl engines/eevee_next/shaders/eevee_cubemap_lib.glsl + engines/eevee_next/shaders/eevee_debug_gbuffer_frag.glsl engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh index cdea6350638..903d26fa2cb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh +++ b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh @@ -21,65 +21,134 @@ class Instance; /** * Full-screen textures containing geometric and surface data. - * Used by deferred shading passes. Only one gbuffer is allocated per view + * Used by deferred shading passes. Only one g-buffer is allocated per view * and is reused for each deferred layer. This is why there can only be temporary * texture inside it. * * Everything is stored inside two array texture, one for each format. This is to fit the * limitation of the number of images we can bind on a single shader. * - * First layer is always for reflection. All parameters to shoot a reflection ray are inside - * this layer. - * - * - Layer 1 : Reflection - * - R : Normal packed X - * - G : Normal packed Y - * - B : Roughness - * - A : Unused (Could be used for anisotropic roughness) - * - * Second layer is either for diffuse or transmission. Material mixing both are not - * physically based and are uncommon. So in order to save bandwidth and texture memory, we only - * store one. We use random sampling to mix between both. All parameters to shoot a refraction - * ray are inside this layer. - * - * - Layer 2 : Refraction - * - R : Normal packed X - * - G : Normal packed Y - * - B : Roughness (isotropic) - * - A : IOR - * - * - Layer 2 : Diffuse / Sub-Surface Scattering - * - R : Normal packed X - * - G : Normal packed Y - * - B : Thickness - * - A : Unused (Could be used for diffuse roughness) - * - * Layer 3 is only allocated if Sub-Surface Scattering is needed. All parameters for - * screen-space scattering are inside this layer. - * - * - Layer 3 : Sub-Surface Scattering - * - R : Scattering radius R - * - G : Scattering radius G - * - B : Scattering radius B - * - A : Object ID + * The content of the g-buffer is polymorphic. A 8bit header specify the layout of the data. + * The first layer is always written to while others are written only if needed using imageStore + * operations reducing the bandwidth needed. + * Except for some special configurations, the g-buffer holds up to 3 closures. * * For each output closure, we also output the color to apply after the lighting computation. * The color is stored with a 2 exponent that allows input color with component higher than 1. * Color degradation is expected to happen in this case. + * + * Here are special configurations: + * + * - Opaque Dielectric: + * - 1 Diffuse lobe and 1 Reflection lobe without anisotropy. + * - Share a single normal. + * - Reflection is not colored. + * - Layout: + * - Color 1 : Diffuse color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Roughness (isotropic) + * - Closure 1 A : Reflection intensity + * + * - Simple Car-paint: (TODO) + * - 2 Reflection lobe without anisotropy. + * - Share a single normal. + * - Coat layer is not colored. + * - Layout: + * - Color 1 : Bottom layer color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Roughness (isotropic) + * - Closure 1 A : Coat layer intensity + * + * - Simple Glass: (TODO) + * - 1 Refraction lobe and 1 Reflection lobe without anisotropy. + * - Share a single normal. + * - Reflection intensity is derived from IOR. + * - Layout: + * - Color 1 : Refraction color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Roughness (isotropic) + * - Closure 1 A : IOR + * + * Here are Closure configurations: + * + * - Reflection (Isotropic): + * - Layout: + * - Color : Reflection color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Roughness + * - Closure 1 A : Unused + * + * - Reflection (Anisotropic): (TODO) + * - Layout: + * - Color : Reflection color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Tangent packed X + * - Closure 1 A : Tangent packed Y + * - Closure 2 R : Roughness X + * - Closure 2 G : Roughness Y + * - Closure 2 B : Unused + * - Closure 2 A : Unused + * + * - Refraction (Isotropic): + * - Layout: + * - Color : Refraction color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Roughness + * - Closure 1 A : IOR + * + * - Diffuse: + * - Layout: + * - Color : Diffuse color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Unused + * - Closure 1 A : Unused (Could be used for diffuse roughness) + * + * - Sub-Surface Scattering: + * - Layout: + * - Color : Diffuse color + * - Closure 1 R : Normal packed X + * - Closure 1 G : Normal packed Y + * - Closure 1 B : Thickness + * - Closure 1 A : Unused (Could be used for diffuse roughness) + * - Closure 2 R : Scattering radius R + * - Closure 2 G : Scattering radius G + * - Closure 2 B : Scattering radius B + * - Closure 2 A : Object ID + * */ struct GBuffer { /* TODO(fclem): Use texture from pool once they support texture array and layer views. */ - Texture header_tx = {"GbufferHeader"}; - Texture closure_tx = {"GbufferClosure"}; - Texture color_tx = {"GbufferColor"}; + Texture header_tx = {"GBufferHeader"}; + Texture closure_tx = {"GBufferClosure"}; + Texture color_tx = {"GBufferColor"}; + /* References to the GBuffer layer range [1..max]. */ + GPUTexture *closure_img_tx = nullptr; + GPUTexture *color_img_tx = nullptr; - void acquire(int2 extent, eClosureBits closure_bits_) + void acquire(int2 extent, int closure_layer_count, int color_layer_count) { - const bool use_sss = (closure_bits_ & CLOSURE_SSS) != 0; - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; - header_tx.ensure_2d(GPU_R8UI, extent, usage); - closure_tx.ensure_2d_array(GPU_RGBA16, extent, use_sss ? 3 : 2, usage); - color_tx.ensure_2d_array(GPU_RGB10_A2, extent, 2, usage); + /* Always allocating 2 layers so that the image view is always valid. */ + closure_layer_count = max_ii(2, closure_layer_count); + color_layer_count = max_ii(2, color_layer_count); + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | + GPU_TEXTURE_USAGE_ATTACHMENT; + header_tx.ensure_2d(GPU_R16UI, extent, usage); + closure_tx.ensure_2d_array(GPU_RGBA16, extent, closure_layer_count, usage); + color_tx.ensure_2d_array(GPU_RGB10_A2, extent, color_layer_count, usage); + /* Ensure layer view for frame-buffer attachment. */ + closure_tx.ensure_layer_views(); + color_tx.ensure_layer_views(); + /* Ensure layer view for image store. */ + closure_img_tx = closure_tx.layer_range_view(1, closure_layer_count - 1); + color_img_tx = color_tx.layer_range_view(1, color_layer_count - 1); } void release() @@ -88,6 +157,9 @@ struct GBuffer { // header_tx.release(); // closure_tx.release(); // color_tx.release(); + + closure_img_tx = nullptr; + color_img_tx = nullptr; } template void bind_resources(PassType &pass) diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index f4cbecf7826..b33adb6acde 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -116,6 +116,7 @@ void WorldVolumePipeline::sync(GPUMaterial *gpumat) world_ps_.init(); world_ps_.state_set(DRW_STATE_WRITE_COLOR); + world_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); inst_.bind_uniform_data(&world_ps_); inst_.volume.bind_properties_buffers(world_ps_); inst_.sampling.bind_resources(world_ps_); @@ -444,16 +445,13 @@ void DeferredLayer::begin_sync() } { gbuffer_ps_.init(); - gbuffer_ps_.clear_stencil(0x00u); - gbuffer_ps_.state_stencil(0xFFu, 0xFFu, 0xFFu); { /* Common resources. */ /* G-buffer. */ - gbuffer_ps_.bind_image(GBUF_CLOSURE_SLOT, &inst_.gbuffer.closure_tx); - gbuffer_ps_.bind_image(GBUF_COLOR_SLOT, &inst_.gbuffer.color_tx); - gbuffer_ps_.bind_image(GBUF_HEADER_SLOT, &inst_.gbuffer.header_tx); + gbuffer_ps_.bind_image(GBUF_CLOSURE_SLOT, &inst_.gbuffer.closure_img_tx); + gbuffer_ps_.bind_image(GBUF_COLOR_SLOT, &inst_.gbuffer.color_img_tx); /* RenderPasses & AOVs. */ gbuffer_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); gbuffer_ps_.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); @@ -469,8 +467,7 @@ void DeferredLayer::begin_sync() inst_.cryptomatte.bind_resources(gbuffer_ps_); } - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_WRITE_STENCIL | - DRW_STATE_STENCIL_ALWAYS; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided"); gbuffer_double_sided_ps_->state_set(state); @@ -489,9 +486,9 @@ void DeferredLayer::end_sync() { PassSimple &pass = eval_light_ps_; pass.init(); - /* Use stencil test to reject pixel not written by this layer. */ - pass.state_set(DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - pass.state_stencil(0x00u, 0x00u, evaluated_closures); + /* Use depth test to reject background pixels. */ + /* WORKAROUND: Avoid rasterizer discard, but the shaders actually use no fragment output. */ + pass.state_set(DRW_STATE_WRITE_STENCIL | DRW_STATE_DEPTH_GREATER); pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT)); pass.bind_image("direct_diffuse_img", &direct_diffuse_tx_); pass.bind_image("direct_reflect_img", &direct_reflect_tx_); @@ -511,9 +508,8 @@ void DeferredLayer::end_sync() { PassSimple &pass = combine_ps_; pass.init(); - /* Use stencil test to reject pixel not written by this layer. */ - pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_BLEND_ADD_FULL); - pass.state_stencil(0x00u, 0x00u, evaluated_closures); + /* Use depth test to reject background pixels. */ + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER | DRW_STATE_BLEND_ADD_FULL); pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_COMBINE)); pass.bind_image("direct_diffuse_img", &direct_diffuse_tx_); pass.bind_image("direct_reflect_img", &direct_reflect_tx_); @@ -552,15 +548,14 @@ PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_single_sided_ps_ : gbuffer_double_sided_ps_; - pass = &pass->sub(GPU_material_get_name(gpumat)); - pass->state_stencil(closure_bits, 0xFFu, 0xFFu); - return pass; + return &pass->sub(GPU_material_get_name(gpumat)); } void DeferredLayer::render(View &main_view, View &render_view, Framebuffer &prepass_fb, Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, int2 extent, RayTraceBuffer &rt_buffer, bool is_first_pass) @@ -601,8 +596,6 @@ void DeferredLayer::render(View &main_view, GPU_framebuffer_bind(prepass_fb); inst_.manager->submit(prepass_ps_, render_view); - inst_.gbuffer.acquire(extent, closure_bits_); - if (closure_bits_ & CLOSURE_AMBIENT_OCCLUSION) { /* If the shader needs Ambient Occlusion, we need to update the HiZ here. */ if (do_screen_space_refraction) { @@ -616,10 +609,23 @@ void DeferredLayer::render(View &main_view, } } - /* TODO(fclem): Clear in pass when Gbuffer will render with framebuffer. */ - inst_.gbuffer.header_tx.clear(uint4(0)); + if (/* FIXME(fclem): Metal doesn't clear the whole framebuffer correctly. */ + GPU_backend_get_type() == GPU_BACKEND_METAL || + /* FIXME(fclem): Vulkan doesn't implement load / store config yet. */ + GPU_backend_get_type() == GPU_BACKEND_VULKAN) + { + inst_.gbuffer.header_tx.clear(int4(0)); + } + + GPU_framebuffer_bind_ex(gbuffer_fb, + { + {GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, /* Depth */ + {GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, /* Combined */ + {GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {0}}, /* GBuf Header */ + {GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Closure */ + {GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Color */ + }); - GPU_framebuffer_bind(combined_fb); inst_.manager->submit(gbuffer_ps_, render_view); inst_.hiz_buffer.set_dirty(); @@ -641,13 +647,13 @@ void DeferredLayer::render(View &main_view, inst_.shadows.set_view(render_view); { - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE | - GPU_TEXTURE_USAGE_ATTACHMENT; + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; direct_diffuse_tx_.acquire(extent, GPU_RGBA16F, usage); direct_reflect_tx_.acquire(extent, GPU_RGBA16F, usage); direct_refract_tx_.acquire(extent, GPU_RGBA16F, usage); } + GPU_framebuffer_bind(combined_fb); inst_.manager->submit(eval_light_ps_, render_view); RayTraceResult diffuse_result = inst_.raytracing.trace(rt_buffer, @@ -688,7 +694,7 @@ void DeferredLayer::render(View &main_view, radiance_feedback_persmat_ = render_view.persmat(); } - inst_.gbuffer.release(); + inst_.pipelines.deferred.debug_draw(render_view, combined_fb); } /** \} */ @@ -709,6 +715,53 @@ void DeferredPipeline::end_sync() { opaque_layer_.end_sync(); refraction_layer_.end_sync(); + + debug_pass_sync(); +} + +void DeferredPipeline::debug_pass_sync() +{ + Instance &inst = opaque_layer_.inst_; + if (!ELEM(inst.debug_mode, + eDebugMode::DEBUG_GBUFFER_EVALUATION, + eDebugMode::DEBUG_GBUFFER_STORAGE)) + { + return; + } + + PassSimple &pass = debug_draw_ps_; + pass.init(); + pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + pass.shader_set(inst.shaders.static_shader_get(DEBUG_GBUFFER)); + pass.push_constant("debug_mode", int(inst.debug_mode)); + inst.gbuffer.bind_resources(pass); + pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); +} + +void DeferredPipeline::debug_draw(draw::View &view, GPUFrameBuffer *combined_fb) +{ + Instance &inst = opaque_layer_.inst_; + if (!ELEM(inst.debug_mode, + eDebugMode::DEBUG_GBUFFER_EVALUATION, + eDebugMode::DEBUG_GBUFFER_STORAGE)) + { + return; + } + + switch (inst.debug_mode) { + case eDebugMode::DEBUG_GBUFFER_EVALUATION: + inst.info = "Debug Mode: Deferred Lighting Cost"; + break; + case eDebugMode::DEBUG_GBUFFER_STORAGE: + inst.info = "Debug Mode: Gbuffer Storage Cost"; + break; + default: + /* Nothing to display. */ + return; + } + + GPU_framebuffer_bind(combined_fb); + inst.manager->submit(debug_draw_ps_, view); } PassMain::Sub *DeferredPipeline::prepass_add(::Material *blender_mat, @@ -737,18 +790,31 @@ void DeferredPipeline::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) { DRW_stats_group_start("Deferred.Opaque"); - opaque_layer_.render( - main_view, render_view, prepass_fb, combined_fb, extent, rt_buffer_opaque_layer, true); + opaque_layer_.render(main_view, + render_view, + prepass_fb, + combined_fb, + gbuffer_fb, + extent, + rt_buffer_opaque_layer, + true); DRW_stats_group_end(); DRW_stats_group_start("Deferred.Refract"); - refraction_layer_.render( - main_view, render_view, prepass_fb, combined_fb, extent, rt_buffer_refract_layer, false); + refraction_layer_.render(main_view, + render_view, + prepass_fb, + combined_fb, + gbuffer_fb, + extent, + rt_buffer_refract_layer, + false); DRW_stats_group_end(); } @@ -1003,9 +1069,6 @@ void DeferredProbeLayer::begin_sync() } { gbuffer_ps_.init(); - gbuffer_ps_.clear_stencil(0x00u); - gbuffer_ps_.state_stencil(0xFFu, 0xFFu, 0xFFu); - { /* Common resources. */ @@ -1028,8 +1091,7 @@ void DeferredProbeLayer::begin_sync() inst_.cryptomatte.bind_resources(gbuffer_ps_); } - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_WRITE_STENCIL | - DRW_STATE_STENCIL_ALWAYS; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided"); gbuffer_double_sided_ps_->state_set(state); @@ -1044,9 +1106,8 @@ void DeferredProbeLayer::end_sync() if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { PassSimple &pass = eval_light_ps_; pass.init(); - /* Use stencil test to reject pixel not written by this layer. */ - pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL); - pass.state_stencil(0x00u, 0x00u, (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)); + /* Use depth test to reject background pixels. */ + pass.state_set(DRW_STATE_DEPTH_GREATER | DRW_STATE_WRITE_COLOR); pass.shader_set(inst_.shaders.static_shader_get(DEFERRED_CAPTURE_EVAL)); pass.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx); pass.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx); @@ -1080,14 +1141,13 @@ PassMain::Sub *DeferredProbeLayer::material_add(::Material *blender_mat, GPUMate PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? gbuffer_single_sided_ps_ : gbuffer_double_sided_ps_; - pass = &pass->sub(GPU_material_get_name(gpumat)); - pass->state_stencil(closure_bits, 0xFFu, 0xFFu); - return pass; + return &pass->sub(GPU_material_get_name(gpumat)); } void DeferredProbeLayer::render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, int2 extent) { GPU_framebuffer_bind(prepass_fb); @@ -1099,14 +1159,11 @@ void DeferredProbeLayer::render(View &view, inst_.shadows.set_view(view); inst_.irradiance_cache.set_view(view); - inst_.gbuffer.acquire(extent, closure_bits_); - - GPU_framebuffer_bind(combined_fb); + GPU_framebuffer_bind(gbuffer_fb); inst_.manager->submit(gbuffer_ps_, view); + GPU_framebuffer_bind(combined_fb); inst_.manager->submit(eval_light_ps_, view); - - inst_.gbuffer.release(); } /** \} */ @@ -1140,10 +1197,11 @@ PassMain::Sub *DeferredProbePipeline::material_add(::Material *blender_mat, GPUM void DeferredProbePipeline::render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, int2 extent) { GPU_debug_group_begin("Probe.Render"); - opaque_layer_.render(view, prepass_fb, combined_fb, extent); + opaque_layer_.render(view, prepass_fb, combined_fb, gbuffer_fb, extent); GPU_debug_group_end(); } @@ -1240,30 +1298,30 @@ PassMain::Sub *PlanarProbePipeline::material_add(::Material *blender_mat, GPUMat return &pass->sub(GPU_material_get_name(gpumat)); } -void PlanarProbePipeline::render(View &view, Framebuffer &combined_fb, int layer_id, int2 extent) +void PlanarProbePipeline::render( + View &view, Framebuffer &gbuffer_fb, Framebuffer &combined_fb, int layer_id, int2 extent) { GPU_debug_group_begin("Planar.Capture"); inst_.hiz_buffer.set_source(&inst_.planar_probes.depth_tx_, layer_id); inst_.hiz_buffer.set_dirty(); - GPU_framebuffer_bind(combined_fb); - GPU_framebuffer_clear_depth(combined_fb, 1.0f); + GPU_framebuffer_bind(gbuffer_fb); + GPU_framebuffer_clear_depth(gbuffer_fb, 1.0f); inst_.manager->submit(prepass_ps_, view); inst_.lights.set_view(view, extent); inst_.shadows.set_view(view); inst_.irradiance_cache.set_view(view); - inst_.gbuffer.acquire(extent, closure_bits_); - inst_.hiz_buffer.update(); - GPU_framebuffer_bind(combined_fb); - GPU_framebuffer_clear_color(combined_fb, float4(0.0f, 0.0f, 0.0f, 1.0f)); - inst_.manager->submit(gbuffer_ps_, view); - inst_.manager->submit(eval_light_ps_, view); - inst_.gbuffer.release(); + GPU_framebuffer_bind(gbuffer_fb); + GPU_framebuffer_clear_color(gbuffer_fb, float4(0.0f, 0.0f, 0.0f, 1.0f)); + inst_.manager->submit(gbuffer_ps_, view); + + GPU_framebuffer_bind(combined_fb); + inst_.manager->submit(eval_light_ps_, view); GPU_debug_group_end(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index 5f189a5bd0e..2456daf806d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -180,9 +180,27 @@ struct DeferredLayerBase { /* Closures bits from the materials in this pass. */ eClosureBits closure_bits_ = CLOSURE_NONE; + + /* Return the amount of gbuffer layer needed. */ + int closure_layer_count() const + { + return count_bits_i(closure_bits_ & + (CLOSURE_REFRACTION | CLOSURE_REFLECTION | CLOSURE_DIFFUSE | CLOSURE_SSS)); + } + + /* Return the amount of gbuffer layer needed. */ + int color_layer_count() const + { + return count_bits_i(closure_bits_ & + (CLOSURE_REFRACTION | CLOSURE_REFLECTION | CLOSURE_DIFFUSE)); + } }; -class DeferredLayer : private DeferredLayerBase { +class DeferredPipeline; + +class DeferredLayer : DeferredLayerBase { + friend DeferredPipeline; + private: Instance &inst_; @@ -225,6 +243,7 @@ class DeferredLayer : private DeferredLayerBase { void render(View &main_view, View &render_view, Framebuffer &prepass_fb, + Framebuffer &gbuffer_fb, Framebuffer &combined_fb, int2 extent, RayTraceBuffer &rt_buffer, @@ -239,6 +258,8 @@ class DeferredPipeline { DeferredLayer refraction_layer_; DeferredLayer volumetric_layer_; + PassSimple debug_draw_ps_ = {"debug_gbuffer"}; + public: DeferredPipeline(Instance &inst) : opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){}; @@ -253,9 +274,27 @@ class DeferredPipeline { View &render_view, Framebuffer &prepass_fb, Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, int2 extent, RayTraceBuffer &rt_buffer_opaque_layer, RayTraceBuffer &rt_buffer_refract_layer); + + /* Return the maximum amount of gbuffer layer needed. */ + int closure_layer_count() const + { + return max_ii(opaque_layer_.closure_layer_count(), refraction_layer_.closure_layer_count()); + } + + /* Return the maximum amount of gbuffer layer needed. */ + int color_layer_count() const + { + return max_ii(opaque_layer_.color_layer_count(), refraction_layer_.color_layer_count()); + } + + void debug_draw(draw::View &view, GPUFrameBuffer *combined_fb); + + private: + void debug_pass_sync(); }; /** \} */ @@ -410,7 +449,12 @@ class VolumePipeline { /* -------------------------------------------------------------------- */ /** \name Deferred Probe Capture. * \{ */ + +class DeferredProbePipeline; + class DeferredProbeLayer : DeferredLayerBase { + friend DeferredProbePipeline; + private: Instance &inst_; @@ -425,7 +469,11 @@ class DeferredProbeLayer : DeferredLayerBase { PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat); PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat); - void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); + void render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, + int2 extent); }; class DeferredProbePipeline { @@ -441,7 +489,23 @@ class DeferredProbePipeline { PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat); PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat); - void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); + void render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + Framebuffer &gbuffer_fb, + int2 extent); + + /* Return the maximum amount of gbuffer layer needed. */ + int closure_layer_count() const + { + return opaque_layer_.closure_layer_count(); + } + + /* Return the maximum amount of gbuffer layer needed. */ + int color_layer_count() const + { + return opaque_layer_.color_layer_count(); + } }; /** \} */ @@ -456,9 +520,6 @@ class PlanarProbePipeline : DeferredLayerBase { PassSimple eval_light_ps_ = {"EvalLights"}; - /* Closures bits from the materials in this pass. */ - eClosureBits closure_bits_ = CLOSURE_NONE; - public: PlanarProbePipeline(Instance &inst) : inst_(inst){}; @@ -468,7 +529,8 @@ class PlanarProbePipeline : DeferredLayerBase { PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat); PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat); - void render(View &view, Framebuffer &combined_fb, int layer_id, int2 extent); + void render( + View &view, Framebuffer &gbuffer, Framebuffer &combined_fb, int layer_id, int2 extent); }; /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc b/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc index fb629dc0670..0d8ee0a0e12 100644 --- a/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_planar_probes.cc @@ -136,10 +136,22 @@ void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_ext world_clip_buf_.plane = probe.reflection_clip_plane_get(); world_clip_buf_.push_update(); + GBuffer &gbuf = instance_.gbuffer; + gbuf.acquire(extent, + instance_.pipelines.deferred.closure_layer_count(), + instance_.pipelines.deferred.color_layer_count()); + res.combined_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx_, resource_index), GPU_ATTACHMENT_TEXTURE_LAYER(radiance_tx_, resource_index)); - instance_.pipelines.planar.render(res.view, res.combined_fb, resource_index, extent); + res.gbuffer_fb.ensure(GPU_ATTACHMENT_TEXTURE_LAYER(depth_tx_, resource_index), + GPU_ATTACHMENT_TEXTURE_LAYER(radiance_tx_, resource_index), + GPU_ATTACHMENT_TEXTURE(gbuf.header_tx), + GPU_ATTACHMENT_TEXTURE_LAYER(gbuf.color_tx.layer_view(0), 0), + GPU_ATTACHMENT_TEXTURE_LAYER(gbuf.closure_tx.layer_view(0), 0)); + + instance_.pipelines.planar.render( + res.view, res.combined_fb, res.gbuffer_fb, resource_index, extent); if (do_display_draw_ && probe.viewport_display) { display_data_buf_.get_or_resize(display_index++) = {probe.plane_to_world, resource_index}; diff --git a/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh b/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh index 615a2e4a319..147ccac991e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_planar_probes.hh @@ -69,6 +69,7 @@ struct PlanarProbe : ProbePlanarData { struct PlanarProbeResources : NonCopyable { Framebuffer combined_fb = {"planar.combined_fb"}; + Framebuffer gbuffer_fb = {"planar.gbuffer_fb"}; draw::View view = {"planar.view"}; }; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 7060cd92de0..d99a982f056 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -128,6 +128,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_debug_surfels"; case DEBUG_IRRADIANCE_GRID: return "eevee_debug_irradiance_grid"; + case DEBUG_GBUFFER: + return "eevee_debug_gbuffer"; case DISPLAY_PROBE_GRID: return "eevee_display_probe_grid"; case DISPLAY_PROBE_REFLECTION: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index b94a152e3eb..5b8eee65e0e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -37,6 +37,7 @@ enum eShaderType { DEFERRED_CAPTURE_EVAL, DEFERRED_PLANAR_EVAL, + DEBUG_GBUFFER, DEBUG_SURFELS, DEBUG_IRRADIANCE_GRID, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index b5f81f3d1d3..a2dc9a31fa8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -78,6 +78,14 @@ enum eDebugMode : uint32_t { * Show random color for each tile. Verify distribution and LOD transitions. */ DEBUG_SHADOW_TILEMAP_RANDOM_COLOR = 13u, + /** + * Show storage cost of each pixel in the gbuffer. + */ + DEBUG_GBUFFER_STORAGE = 14u, + /** + * Show evaluation cost of each pixel. + */ + DEBUG_GBUFFER_EVALUATION = 15u, }; /** \} */ @@ -1170,12 +1178,10 @@ BLI_STATIC_ASSERT_ALIGN(HiZData, 16) enum eClosureBits : uint32_t { CLOSURE_NONE = 0u, - /** NOTE: These are used as stencil bits. So we are limited to 8bits. */ CLOSURE_DIFFUSE = (1u << 0u), CLOSURE_SSS = (1u << 1u), CLOSURE_REFLECTION = (1u << 2u), CLOSURE_REFRACTION = (1u << 3u), - /* Non-stencil bits. */ CLOSURE_TRANSPARENCY = (1u << 8u), CLOSURE_EMISSION = (1u << 9u), CLOSURE_HOLDOUT = (1u << 10u), @@ -1183,6 +1189,24 @@ enum eClosureBits : uint32_t { CLOSURE_AMBIENT_OCCLUSION = (1u << 12u), }; +enum GBufferMode : uint32_t { + /** None mode for pixels not rendered. */ + GBUF_NONE = 0u, + + GBUF_REFLECTION = 1u, + GBUF_REFRACTION = 2u, + GBUF_DIFFUSE = 3u, + GBUF_SSS = 4u, + + /** Special configurations. Packs multiple closures into 1 layer. */ + GBUF_OPAQUE_DIELECTRIC = 4u, + + /** Set for surfaces without lit closures. This stores only the normal to the surface. */ + GBUF_UNLIT = 15u, + + /** IMPORTANT: Needs to be less than 16 for correct packing in g-buffer header. */ +}; + struct RayTraceData { /** ViewProjection matrix used to render the previous frame. */ float4x4 history_persmat; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index a7490733496..dad355bda90 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -78,19 +78,32 @@ void ShadingView::render() return; } + update_view(); + + DRW_stats_group_start(name_); + + /* Needs to be before anything else because it query its own gbuffer. */ + inst_.planar_probes.set_view(render_view_, extent_); + /* Query temp textures and create frame-buffers. */ RenderBuffers &rbufs = inst_.render_buffers; rbufs.acquire(extent_); + combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx)); prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), GPU_ATTACHMENT_TEXTURE(rbufs.vector_tx)); - update_view(); + GBuffer &gbuf = inst_.gbuffer; + gbuf.acquire(extent_, + inst_.pipelines.deferred.closure_layer_count(), + inst_.pipelines.deferred.color_layer_count()); - DRW_stats_group_start(name_); - - inst_.planar_probes.set_view(render_view_, extent_); + gbuffer_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), + GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx), + GPU_ATTACHMENT_TEXTURE(gbuf.header_tx), + GPU_ATTACHMENT_TEXTURE_LAYER(gbuf.color_tx.layer_view(0), 0), + GPU_ATTACHMENT_TEXTURE_LAYER(gbuf.closure_tx.layer_view(0), 0)); /* If camera has any motion, compute motion vector in the film pass. Otherwise, we avoid float * precision issue by setting the motion of all static geometry to 0. */ @@ -119,10 +132,13 @@ void ShadingView::render() render_view_, prepass_fb_, combined_fb_, + gbuffer_fb_, extent_, rt_buffer_opaque_, rt_buffer_refract_); + inst_.gbuffer.release(); + inst_.volume.draw_compute(render_view_); // inst_.lookdev.render_overlay(view_fb_); @@ -231,10 +247,10 @@ void CaptureView::render_world() update_info->clipping_distances.y); view.sync(view_m4, win_m4); - capture_fb_.ensure( + combined_fb_.ensure( GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face)); - GPU_framebuffer_bind(capture_fb_); + GPU_framebuffer_bind(combined_fb_); inst_.pipelines.world.render(view); } @@ -267,6 +283,10 @@ void CaptureView::render_probes() prepass_fb.ensure(GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx), GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.vector_tx)); + inst_.gbuffer.acquire(extent, + inst_.pipelines.probe.closure_layer_count(), + inst_.pipelines.probe.color_layer_count()); + for (int face : IndexRange(6)) { float4x4 view_m4 = cubeface_mat(face); view_m4 = math::translate(view_m4, -update_info->probe_pos); @@ -278,16 +298,24 @@ void CaptureView::render_probes() update_info->clipping_distances.y); view.sync(view_m4, win_m4); - capture_fb_.ensure( + combined_fb_.ensure( GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx), GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face)); - GPU_framebuffer_bind(capture_fb_); - GPU_framebuffer_clear_color_depth(capture_fb_, float4(0.0f, 0.0f, 0.0f, 1.0f), 1.0f); - inst_.pipelines.probe.render(view, prepass_fb, capture_fb_, extent); + gbuffer_fb_.ensure( + GPU_ATTACHMENT_TEXTURE(inst_.render_buffers.depth_tx), + GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face), + GPU_ATTACHMENT_TEXTURE(inst_.gbuffer.header_tx), + GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.color_tx.layer_view(0), 0), + GPU_ATTACHMENT_TEXTURE_LAYER(inst_.gbuffer.closure_tx.layer_view(0), 0)); + + GPU_framebuffer_bind(combined_fb_); + GPU_framebuffer_clear_color_depth(combined_fb_, float4(0.0f, 0.0f, 0.0f, 1.0f), 1.0f); + inst_.pipelines.probe.render(view, prepass_fb, combined_fb_, gbuffer_fb_, extent); } inst_.render_buffers.release(); + inst_.gbuffer.release(); GPU_debug_group_end(); inst_.reflection_probes.remap_to_octahedral_projection(update_info->atlas_coord); } diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index c45151c90f1..469c1c14fdb 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -46,8 +46,9 @@ class ShadingView { RayTraceBuffer rt_buffer_refract_; DepthOfFieldBuffer dof_buffer_; - Framebuffer prepass_fb_; - Framebuffer combined_fb_; + Framebuffer prepass_fb_ = {"prepass_fb_"}; + Framebuffer combined_fb_ = {"combined_fb_"}; + Framebuffer gbuffer_fb_ = {"gbuffer_fb_"}; Framebuffer transparent_fb_ = {"transparent"}; TextureFromPool postfx_tx_; @@ -153,7 +154,8 @@ class MainView { class CaptureView { private: Instance &inst_; - Framebuffer capture_fb_ = {"World.Capture"}; + Framebuffer combined_fb_ = {"Capture.Combined"}; + Framebuffer gbuffer_fb_ = {"Capture.Gbuffer"}; public: CaptureView(Instance &inst) : inst_(inst) {} @@ -163,22 +165,4 @@ class CaptureView { /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Capture Planar View - * - * View for capturing planar probes outside a ShadingView. - * \{ */ - -class CapturePlanarView { - private: - Instance &inst_; - Framebuffer capture_fb_ = {"Planar.Capture"}; - - public: - CapturePlanarView(Instance &inst) : inst_(inst) {} - void render_probes(); -}; - -/** \} */ - } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_gbuffer_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_gbuffer_frag.glsl new file mode 100644 index 00000000000..30738a4def0 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_gbuffer_frag.glsl @@ -0,0 +1,46 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * Combine light passes to the combined color target and apply surface colors. + * This also fills the different render passes. + */ + +#pragma BLENDER_REQUIRE(draw_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_renderpass_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_debug_gradients_lib.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + + GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel); + + if (gbuf.header == 0u) { + discard; + return; + } + + float shade = saturate(drw_normal_world_to_view(gbuf.surface_N).z); + + uvec4 closure_types = (uvec4(gbuf.header) >> uvec4(0u, 4u, 8u, 12u)) & 15u; + float storage_cost = reduce_add(vec4(not(equal(closure_types, uvec4(0u))))); + float eval_cost = reduce_add(vec4(equal(closure_types, uvec4(GBUF_REFLECTION)))) * 1.0 + + reduce_add(vec4(equal(closure_types, uvec4(GBUF_REFRACTION)))) * 1.0 + + reduce_add(vec4(equal(closure_types, uvec4(GBUF_DIFFUSE)))) * 1.0 + + reduce_add(vec4(equal(closure_types, uvec4(GBUF_SSS)))) * 1.0; + + switch (eDebugMode(debug_mode)) { + default: + case DEBUG_GBUFFER_STORAGE: + out_color_add = shade * vec4(green_to_red_gradient(storage_cost / 4.0), 0.0); + break; + case DEBUG_GBUFFER_EVALUATION: + out_color_add = shade * vec4(green_to_red_gradient(eval_cost / 4.0), 0.0); + break; + } + + out_color_mul = vec4(0.0); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl index 23c8a72b384..8955b86ff00 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_capture_frag.glsl @@ -19,6 +19,10 @@ void main() GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel); + if (!gbuf.has_reflection && !gbuf.has_reflection && !gbuf.has_refraction) { + return; + } + ClosureLightStack stack; stack.cl[0].N = gbuf.has_diffuse ? gbuf.diffuse.N : gbuf.reflection.N; stack.cl[0].ltc_mat = LTC_LAMBERT_MAT; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl index 362c861fdca..f5e0deee8d0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_combine_frag.glsl @@ -9,6 +9,7 @@ #pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(eevee_renderpass_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) void main() { @@ -44,4 +45,10 @@ void main() out_combined.xyz += diffuse_light * gbuf.diffuse.color; out_combined.xyz += reflect_light * gbuf.reflection.color; out_combined.xyz += refract_light * gbuf.refraction.color; + + if (any(isnan(out_combined))) { + out_combined = vec4(1.0, 0.0, 1.0, 0.0); + } + + out_combined = colorspace_safe_color(out_combined); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl index e3716d5c3e8..77f76e7fa44 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -20,6 +20,10 @@ void main() float depth = texelFetch(hiz_tx, texel, 0).r; GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel); + if (!gbuf.has_reflection && !gbuf.has_diffuse /* TODO(fclem) && !gbuf.has_refraction */) { + return; + } + vec3 P = drw_point_screen_to_world(vec3(uvcoordsvar.xy, depth)); /* Assume reflection closure normal is always somewhat representative of the geometric normal. * Ng is only used for shadow biases and subsurface check in this case. */ @@ -91,8 +95,12 @@ void main() vec3 shadows = radiance_shadowed * safe_rcp(radiance_unshadowed); output_renderpass_value(uniform_buf.render_pass.shadow_id, average(shadows)); - imageStore(direct_diffuse_img, texel, vec4(radiance_diffuse, 1.0)); - imageStore(direct_reflect_img, texel, vec4(radiance_specular, 1.0)); + if (gbuf.has_diffuse) { + imageStore(direct_diffuse_img, texel, vec4(radiance_diffuse, 1.0)); + } + if (gbuf.has_reflection) { + imageStore(direct_reflect_img, texel, vec4(radiance_specular, 1.0)); + } /* TODO(fclem): Support LTC for refraction. */ // imageStore(direct_refract_img, texel, vec4(cl_refr.light_shadowed, 1.0)); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl index f5546da7338..1587a2b25b2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -113,14 +113,160 @@ bool gbuffer_is_refraction(vec4 gbuffer) return gbuffer.w < 1.0; } +uint gbuffer_header_pack(GBufferMode mode, uint layer) +{ + return (mode << (4u * layer)); +} + +GBufferMode gbuffer_header_unpack(uint data, uint layer) +{ + return GBufferMode((data >> (4u * layer)) & 15u); +} + +/* Return true if any layer of the gbuffer match the given closure. */ +bool gbuffer_has_closure(uint header, eClosureBits closure) +{ + int layer = 0; + + /* Check special configurations first. */ + + if (gbuffer_header_unpack(header, layer) == GBUF_OPAQUE_DIELECTRIC) { + if (closure == eClosureBits(CLOSURE_DIFFUSE)) { + return true; + } + if (closure == eClosureBits(CLOSURE_REFLECTION)) { + return true; + } + return false; + } + + /* Since closure order in the gbuffer is static, we check them in order. */ + + bool has_refraction = (gbuffer_header_unpack(header, layer) == GBUF_REFRACTION); + layer += int(has_refraction); + + if (closure == eClosureBits(CLOSURE_REFRACTION)) { + return has_refraction; + } + + bool has_reflection = (gbuffer_header_unpack(header, layer) == GBUF_REFLECTION); + layer += int(has_reflection); + + if (closure == eClosureBits(CLOSURE_REFLECTION)) { + return has_reflection; + } + + bool has_diffuse = (gbuffer_header_unpack(header, layer) == GBUF_DIFFUSE); + layer += int(has_diffuse); + + if (closure == eClosureBits(CLOSURE_DIFFUSE)) { + return has_diffuse; + } + + return false; +} + +struct GBufferDataPacked { + uint header; + /* TODO(fclem): Resize arrays based on used closures. */ + vec4 closure[4]; + vec4 color[3]; +}; + +GBufferDataPacked gbuffer_pack(ClosureDiffuse diffuse, + ClosureReflection reflection, + ClosureRefraction refraction, + vec3 default_N, + float thickness) +{ + GBufferDataPacked gbuf; + gbuf.header = 0u; + + bool has_refraction = refraction.weight > 1e-5; + bool has_reflection = reflection.weight > 1e-5; + bool has_diffuse = diffuse.weight > 1e-5; + bool has_sss = diffuse.sss_id > 0; + + int layer = 0; + + /* Check special configurations first. */ + + /* Opaque Dielectric. */ + if (!has_refraction && !has_sss && has_reflection && has_diffuse) { + /* TODO(fclem): Compute this only if needed (guarded under ifdefs). */ + bool has_shared_normal = all(equal(diffuse.N, reflection.N)); + bool has_colorless_reflection = all(equal(reflection.color.rgb, reflection.color.gbr)); + if (has_shared_normal && has_colorless_reflection) { + gbuf.color[layer] = gbuffer_color_pack(diffuse.color); + gbuf.closure[layer].xy = gbuffer_normal_pack(diffuse.N); + gbuf.closure[layer].z = reflection.roughness; + /* Supports weight > 1.0. Same precision as 10bit. */ + gbuf.closure[layer].w = reflection.color.r * (1.0 / 16.0); + gbuf.header = gbuffer_header_pack(GBUF_OPAQUE_DIELECTRIC, layer); + return gbuf; + } + } + + if (has_refraction) { + gbuf.color[layer] = gbuffer_color_pack(refraction.color); + gbuf.closure[layer].xy = gbuffer_normal_pack(refraction.N); + gbuf.closure[layer].z = refraction.roughness; + gbuf.closure[layer].w = gbuffer_ior_pack(refraction.ior); + gbuf.header |= gbuffer_header_pack(GBUF_REFRACTION, layer); + layer += 1; + } + + if (has_reflection) { + gbuf.color[layer] = gbuffer_color_pack(reflection.color); + gbuf.closure[layer].xy = gbuffer_normal_pack(reflection.N); + gbuf.closure[layer].z = reflection.roughness; + gbuf.closure[layer].w = 0.0; /* Unused. */ + gbuf.header |= gbuffer_header_pack(GBUF_REFLECTION, layer); + layer += 1; + } + + if (has_diffuse) { + gbuf.color[layer] = gbuffer_color_pack(diffuse.color); + gbuf.closure[layer].xy = gbuffer_normal_pack(diffuse.N); + gbuf.closure[layer].z = 0.0; /* Unused. */ + gbuf.closure[layer].w = gbuffer_thickness_pack(thickness); + gbuf.header |= gbuffer_header_pack(GBUF_DIFFUSE, layer); + layer += 1; + } + + if (has_sss) { + gbuf.closure[layer].xyz = gbuffer_sss_radii_pack(diffuse.sss_radius); + gbuf.closure[layer].w = gbuffer_object_id_unorm16_pack(diffuse.sss_id); + gbuf.header |= gbuffer_header_pack(GBUF_SSS, layer); + layer += 1; + } + + if (layer == 0) { + /* If no lit BDSF is outputed, still output the surface normal in the first layer. + * This is needed by some algorithms. */ + gbuf.color[layer] = vec4(0.0); + gbuf.closure[layer].xy = gbuffer_normal_pack(default_N); + gbuf.closure[layer].z = 0.0; /* Unused. */ + gbuf.closure[layer].w = 0.0; /* Unused. */ + gbuf.header |= gbuffer_header_pack(GBUF_UNLIT, layer); + } + + return gbuf; +} + struct GBufferData { + /* Only valid (or null) if `has_diffuse`, `has_reflection` or `has_refraction` is true. */ ClosureDiffuse diffuse; ClosureReflection reflection; ClosureRefraction refraction; + /* First world normal stored in the gbuffer. Only valid if `has_any_surface` is true. */ + vec3 surface_N; float thickness; bool has_diffuse; bool has_reflection; bool has_refraction; + bool has_any_surface; + uint header; }; GBufferData gbuffer_read(usampler2D header_tx, @@ -130,66 +276,122 @@ GBufferData gbuffer_read(usampler2D header_tx, { GBufferData gbuf; - uint header = texelFetch(header_tx, texel, 0).r; + gbuf.header = texelFetch(header_tx, texel, 0).r; + gbuf.has_any_surface = (gbuf.header != 0u); + + if (!gbuf.has_any_surface) { + gbuf.has_diffuse = false; + gbuf.has_reflection = false; + gbuf.has_refraction = false; + return gbuf; + } gbuf.thickness = 0.0; - gbuf.has_diffuse = flag_test(header, CLOSURE_DIFFUSE); - gbuf.has_reflection = flag_test(header, CLOSURE_REFLECTION); - gbuf.has_refraction = flag_test(header, CLOSURE_REFRACTION); - if (gbuf.has_reflection) { - int layer = 0; + /* First closure is always written. */ + gbuf.surface_N = gbuffer_normal_unpack(texelFetch(closure_tx, ivec3(texel, 0), 0).xy); + + int layer = 0; + + /* Check special configurations first. */ + + if (gbuffer_header_unpack(gbuf.header, layer) == GBUF_OPAQUE_DIELECTRIC) { vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); - gbuf.reflection.N = gbuffer_normal_unpack(closure_packed.xy); - gbuf.reflection.roughness = closure_packed.z; - vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); - gbuf.reflection.color = gbuffer_color_unpack(color_packed); - } - else { - gbuf.reflection.N = vec3(0.0, 0.0, 1.0); - gbuf.reflection.roughness = 0.0; - gbuf.reflection.color = vec3(0.0); - } - if (gbuf.has_diffuse) { - int layer = 1; - vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); - gbuf.diffuse.N = gbuffer_normal_unpack(closure_packed.xy); - gbuf.diffuse.sss_id = 0u; - gbuf.thickness = gbuffer_thickness_unpack(closure_packed.z); - - vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); gbuf.diffuse.color = gbuffer_color_unpack(color_packed); + gbuf.has_diffuse = true; - if (flag_test(header, CLOSURE_SSS)) { - int layer = 2; - vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); - gbuf.diffuse.sss_radius = gbuffer_sss_radii_unpack(closure_packed.xyz); - gbuf.diffuse.sss_id = gbuffer_object_id_unorm16_unpack(closure_packed.w); - } - } - else { - gbuf.diffuse.N = vec3(0.0, 0.0, 1.0); + gbuf.reflection.color = vec3(closure_packed.w * 16.0); + gbuf.reflection.N = gbuf.diffuse.N = gbuffer_normal_unpack(closure_packed.xy); + gbuf.reflection.roughness = closure_packed.z; + gbuf.has_reflection = true; + + /* Default values. */ + gbuf.refraction.color = vec3(0.0); + gbuf.refraction.N = vec3(0.0, 0.0, 1.0); + gbuf.refraction.roughness = 0.0; + gbuf.refraction.ior = 1.1; + gbuf.has_refraction = false; + + /* Default values. */ + gbuf.diffuse.sss_radius = vec3(0.0, 0.0, 0.0); gbuf.diffuse.sss_id = 0u; - gbuf.diffuse.color = vec3(0.0); + + return gbuf; } + /* Since closure order in the gbuffer is static, we check them in order. */ + + gbuf.has_refraction = (gbuffer_header_unpack(gbuf.header, layer) == GBUF_REFRACTION); + if (gbuf.has_refraction) { - int layer = 1; vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); + vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); + + gbuf.refraction.color = gbuffer_color_unpack(color_packed); gbuf.refraction.N = gbuffer_normal_unpack(closure_packed.xy); gbuf.refraction.roughness = closure_packed.z; gbuf.refraction.ior = gbuffer_ior_unpack(closure_packed.w); - - vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); - gbuf.refraction.color = gbuffer_color_unpack(color_packed); + layer += 1; } else { - gbuf.refraction.N = vec3(0.0, 0.0, 1.0); - gbuf.refraction.ior = 1.1; - gbuf.refraction.roughness = 0.0; + /* Default values. */ gbuf.refraction.color = vec3(0.0); + gbuf.refraction.N = vec3(0.0, 0.0, 1.0); + gbuf.refraction.roughness = 0.0; + gbuf.refraction.ior = 1.1; + } + + gbuf.has_reflection = (gbuffer_header_unpack(gbuf.header, layer) == GBUF_REFLECTION); + + if (gbuf.has_reflection) { + vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); + vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); + + gbuf.reflection.color = gbuffer_color_unpack(color_packed); + gbuf.reflection.N = gbuffer_normal_unpack(closure_packed.xy); + gbuf.reflection.roughness = closure_packed.z; + layer += 1; + } + else { + /* Default values. */ + gbuf.reflection.color = vec3(0.0); + gbuf.reflection.N = vec3(0.0, 0.0, 1.0); + gbuf.reflection.roughness = 0.0; + } + + gbuf.has_diffuse = (gbuffer_header_unpack(gbuf.header, layer) == GBUF_DIFFUSE); + + if (gbuf.has_diffuse) { + vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); + vec4 color_packed = texelFetch(color_tx, ivec3(texel, layer), 0); + + gbuf.diffuse.color = gbuffer_color_unpack(color_packed); + gbuf.diffuse.N = gbuffer_normal_unpack(closure_packed.xy); + gbuf.thickness = gbuffer_thickness_unpack(closure_packed.w); + layer += 1; + } + else { + /* Default values. */ + gbuf.diffuse.color = vec3(0.0); + gbuf.diffuse.N = vec3(0.0, 0.0, 1.0); + gbuf.thickness = 0.0; + } + + bool has_sss = (gbuffer_header_unpack(gbuf.header, layer) == GBUF_SSS); + + if (has_sss) { + vec4 closure_packed = texelFetch(closure_tx, ivec3(texel, layer), 0); + + gbuf.diffuse.sss_radius = gbuffer_sss_radii_unpack(closure_packed.xyz); + gbuf.diffuse.sss_id = gbuffer_object_id_unorm16_unpack(closure_packed.w); + layer += 1; + } + else { + /* Default values. */ + gbuf.diffuse.sss_radius = vec3(0.0, 0.0, 0.0); + gbuf.diffuse.sss_id = 0u; } return gbuf; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl index 90ea5ef20cb..79dc30e81d5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_horizon_denoise_comp.glsl @@ -62,10 +62,10 @@ vec3 load_normal(ivec2 texel) if (gbuf.has_diffuse) { N = gbuf.diffuse.N; } - if (gbuf.has_reflection) { + else if (gbuf.has_reflection) { N = gbuf.reflection.N; } - if (gbuf.has_refraction) { + else if (gbuf.has_refraction) { N = gbuf.refraction.N; } return N; @@ -95,20 +95,26 @@ void main() GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel_fullres); - uint closure_bits = texelFetch(gbuf_header_tx, texel_fullres, 0).r; - if (!flag_test(closure_bits, uniform_buf.raytrace.closure_active)) { - return; - } - vec3 center_N = gbuf.diffuse.N; float roughness = 1.0; if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFLECTION)) { roughness = gbuf.reflection.roughness; center_N = gbuf.reflection.N; + if (!gbuf.has_reflection) { + return; + } } - if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) { + else if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) { roughness = 1.0; /* TODO(fclem): Apparent roughness. */ center_N = gbuf.refraction.N; + if (!gbuf.has_refraction) { + return; + } + } + else /* if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_DIFFUSE)) */ { + if (!gbuf.has_diffuse) { + return; + } } float mix_fac = saturate(roughness * uniform_buf.raytrace.roughness_mask_scale - diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl index 7f3cd75fc30..10af05701d9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_denoise_spatial_comp.glsl @@ -76,8 +76,8 @@ void main() } bool valid_texel = in_texture_range(texel_fullres, gbuf_header_tx); - uint closure_bits = (!valid_texel) ? 0u : texelFetch(gbuf_header_tx, texel_fullres, 0).r; - if (!flag_test(closure_bits, CLOSURE_ACTIVE)) { + uint header = (!valid_texel) ? 0u : texelFetch(gbuf_header_tx, texel_fullres, 0).r; + if (!gbuffer_has_closure(header, eClosureBits(CLOSURE_ACTIVE))) { imageStore(out_radiance_img, texel_fullres, vec4(FLT_11_11_10_MAX, 0.0)); imageStore(out_variance_img, texel_fullres, vec4(0.0)); imageStore(out_hit_depth_img, texel_fullres, vec4(0.0)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl index 3a2af2e4bc2..e6fa67c12e0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ray_tile_classify_comp.glsl @@ -50,16 +50,16 @@ void main() ivec2 texel = ivec2(gl_GlobalInvocationID.xy); bool valid_texel = in_texture_range(texel, gbuf_header_tx); - uint closure_bits = (!valid_texel) ? 0u : texelFetch(gbuf_header_tx, texel, 0).r; + uint header = (!valid_texel) ? 0u : texelFetch(gbuf_header_tx, texel, 0).r; - if (flag_test(closure_bits, uniform_buf.raytrace.closure_active)) { + if (gbuffer_has_closure(header, uniform_buf.raytrace.closure_active)) { GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel); float roughness = 1.0; if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFLECTION)) { roughness = gbuf.reflection.roughness; } - if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) { + else if (uniform_buf.raytrace.closure_active == eClosureBits(CLOSURE_REFRACTION)) { roughness = 0.0; /* TODO(fclem): Apparent roughness. For now, always raytrace. */ } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl index 366ca7aaafd..88b2dc6f127 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl @@ -82,62 +82,28 @@ void main() /* ----- GBuffer output ----- */ - uint header = 0u; + GBufferDataPacked gbuf = gbuffer_pack( + g_diffuse_data, g_reflection_data, g_refraction_data, out_normal, thickness); - if (g_reflection_data.weight > 0.0) { - /* Reflection. */ - vec4 closure; - closure.xy = gbuffer_normal_pack(g_reflection_data.N); - closure.z = g_reflection_data.roughness; - closure.w = 0.0; - imageStore(out_gbuf_closure_img, ivec3(out_texel, 0), closure); + /* Output header and first closure using framebuffer attachment. */ + out_gbuf_header = gbuf.header; + out_gbuf_color = gbuf.color[0]; + out_gbuf_closure = gbuf.closure[0]; - vec4 color = gbuffer_color_pack(g_reflection_data.color); - imageStore(out_gbuf_color_img, ivec3(out_texel, 0), color); - header |= CLOSURE_REFLECTION; + /* Output remaining closures using image store. */ + /* NOTE: The image view start at layer 1 so all destination layer is `closure_index - 1`. */ + if (gbuffer_header_unpack(gbuf.header, 1) != GBUF_NONE) { + imageStore(out_gbuf_color_img, ivec3(out_texel, 1 - 1), gbuf.color[1]); + imageStore(out_gbuf_closure_img, ivec3(out_texel, 1 - 1), gbuf.closure[1]); } - - float combined_weight = g_refraction_data.weight + g_diffuse_data.weight; - if (combined_weight > 0.0) { - /* TODO(fclem) other RNG. */ - float refract_rand = fract(g_closure_rand * 6.1803398875); - bool output_refraction = (refract_rand * combined_weight) < g_refraction_data.weight; - if (output_refraction) { - /* Refraction. */ - vec4 closure; - closure.xy = gbuffer_normal_pack(g_refraction_data.N); - closure.z = g_refraction_data.roughness; - closure.w = gbuffer_ior_pack(g_refraction_data.ior); - imageStore(out_gbuf_closure_img, ivec3(out_texel, 1), closure); - - vec4 color = gbuffer_color_pack(g_refraction_data.color); - imageStore(out_gbuf_color_img, ivec3(out_texel, 1), color); - header |= CLOSURE_REFRACTION; - } - else { - /* Diffuse. */ - vec4 closure; - closure.xy = gbuffer_normal_pack(g_diffuse_data.N); - closure.z = gbuffer_thickness_pack(thickness); - closure.w = 0.0; /* Unused. */ - imageStore(out_gbuf_closure_img, ivec3(out_texel, 1), closure); - - vec4 color = gbuffer_color_pack(g_diffuse_data.color); - imageStore(out_gbuf_color_img, ivec3(out_texel, 1), color); - header |= CLOSURE_DIFFUSE; - } - - if (g_diffuse_data.sss_id > 0) { - /* SubSurface Scattering. */ - vec4 closure; - closure.xyz = gbuffer_sss_radii_pack(g_diffuse_data.sss_radius); - closure.w = gbuffer_object_id_unorm16_pack(uint(resource_id)); - imageStore(out_gbuf_closure_img, ivec3(out_texel, 2), closure); - header |= CLOSURE_SSS; - } + if (gbuffer_header_unpack(gbuf.header, 2) != GBUF_NONE) { + imageStore(out_gbuf_color_img, ivec3(out_texel, 2 - 1), gbuf.color[2]); + imageStore(out_gbuf_closure_img, ivec3(out_texel, 2 - 1), gbuf.closure[2]); + } + if (gbuffer_header_unpack(gbuf.header, 3) != GBUF_NONE) { + /* No color for SSS. */ + imageStore(out_gbuf_closure_img, ivec3(out_texel, 3 - 1), gbuf.closure[3]); } - - imageStore(out_gbuf_header_img, out_texel, uvec4(header)); /* ----- Radiance output ----- */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh index 41906b819d9..db93eb04132 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -100,3 +100,17 @@ GPU_SHADER_CREATE_INFO(eevee_deferred_planar_eval) #undef image_out #undef image_in + +/* -------------------------------------------------------------------- */ +/** \name Debug + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_debug_gbuffer) + .do_static_compilation(true) + .fragment_out(0, Type::VEC4, "out_color_add", DualBlend::SRC_0) + .fragment_out(0, Type::VEC4, "out_color_mul", DualBlend::SRC_1) + .push_constant(Type::INT, "debug_mode") + .fragment_source("eevee_debug_gbuffer_frag.glsl") + .additional_info("draw_view", "draw_fullscreen", "eevee_shared", "eevee_gbuffer_data"); + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 3ab9e0a4855..a1d10371e26 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -153,11 +153,13 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .early_fragment_test(true) /* Direct output. (Emissive, Holdout) */ .fragment_out(0, Type::VEC4, "out_radiance") + .fragment_out(1, Type::UINT, "out_gbuf_header") + .fragment_out(2, Type::VEC4, "out_gbuf_color") + .fragment_out(3, Type::VEC4, "out_gbuf_closure") /* Everything is stored inside a two layered target, one for each format. This is to fit the * limitation of the number of images we can bind on a single shader. */ .image_array_out(GBUF_CLOSURE_SLOT, Qualifier::WRITE, GPU_RGBA16, "out_gbuf_closure_img") .image_array_out(GBUF_COLOR_SLOT, Qualifier::WRITE, GPU_RGB10_A2, "out_gbuf_color_img") - .image(GBUF_HEADER_SLOT, GPU_R8UI, Qualifier::WRITE, ImageType::UINT_2D, "out_gbuf_header_img") .fragment_source("eevee_surf_deferred_frag.glsl") .additional_info("eevee_global_ubo", "eevee_utility_texture", diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 6812f0e5a08..6250a672a64 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -529,6 +529,7 @@ class Texture : NonCopyable { GPUTexture *stencil_view_ = nullptr; Vector mip_views_; Vector layer_views_; + GPUTexture *layer_range_view_ = nullptr; const char *name_; public: @@ -626,12 +627,14 @@ class Texture : NonCopyable { this->tx_ = a.tx_; this->name_ = a.name_; this->stencil_view_ = a.stencil_view_; + this->layer_range_view_ = a.layer_range_view_; this->mip_views_ = std::move(a.mip_views_); this->layer_views_ = std::move(a.layer_views_); a.tx_ = nullptr; a.name_ = nullptr; a.stencil_view_ = nullptr; + a.layer_range_view_ = nullptr; a.mip_views_.clear(); a.layer_views_.clear(); } @@ -769,7 +772,7 @@ class Texture : NonCopyable { } /** - * Ensure the availability of mipmap views. + * Ensure the availability of layer views. * Layer views covers all layers of array textures. * Returns true if the views were (re)created. */ @@ -805,6 +808,33 @@ class Texture : NonCopyable { return stencil_view_; } + /** + * Layer range view cover only the given range. + * This can only called to create one range. + * View is recreated if: + * - The source texture is recreated. + * - The layer_len is different from the last call the this function. + * IMPORTANT: It is not recreated if the layer_start is different from the last call. + * IMPORTANT: If this view is recreated any reference to it should be updated. + */ + GPUTexture *layer_range_view(int layer_start, int layer_len, bool cube_as_array = false) + { + BLI_assert(this->is_valid()); + /* Make sure the range is valid as the GPU_texture_layer_count only returns the effective + * (clipped) range and not the requested range. */ + BLI_assert_msg((layer_start + layer_len) <= GPU_texture_layer_count(tx_), + "Layer range needs to be valid"); + + int view_layer_len = (layer_range_view_) ? GPU_texture_layer_count(layer_range_view_) : -1; + if (layer_len != view_layer_len) { + GPU_TEXTURE_FREE_SAFE(layer_range_view_); + eGPUTextureFormat format = GPU_texture_format(tx_); + layer_range_view_ = GPU_texture_create_view( + name_, tx_, format, 0, 9999, layer_start, layer_len, cube_as_array, false); + } + return layer_range_view_; + } + /** * Returns true if the texture has been allocated or acquired from the pool. */ @@ -935,6 +965,7 @@ class Texture : NonCopyable { GPU_TEXTURE_FREE_SAFE(view); } GPU_TEXTURE_FREE_SAFE(stencil_view_); + GPU_TEXTURE_FREE_SAFE(layer_range_view_); mip_views_.clear(); layer_views_.clear(); } diff --git a/source/blender/gpu/shaders/common/gpu_shader_debug_gradients_lib.glsl b/source/blender/gpu/shaders/common/gpu_shader_debug_gradients_lib.glsl index defe44ebe60..399e2c00b6f 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_debug_gradients_lib.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_debug_gradients_lib.glsl @@ -26,3 +26,7 @@ vec3 hue_gradient(float t) vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0); return (clamp(p - 1.0, 0.0, 1.0)); } +vec3 green_to_red_gradient(float t) +{ + return mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), t); +}