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
This commit is contained in:
Clément Foucault
2023-12-01 14:41:13 +01:00
committed by Clément Foucault
parent 900f9283a3
commit fe848ce3ef
24 changed files with 785 additions and 250 deletions
+1
View File
@@ -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
@@ -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<typename PassType> void bind_resources(PassType &pass)
@@ -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();
}
@@ -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);
};
/** \} */
@@ -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};
@@ -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"};
};
@@ -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:
@@ -37,6 +37,7 @@ enum eShaderType {
DEFERRED_CAPTURE_EVAL,
DEFERRED_PLANAR_EVAL,
DEBUG_GBUFFER,
DEBUG_SURFELS,
DEBUG_IRRADIANCE_GRID,
@@ -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;
@@ -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);
}
@@ -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
@@ -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);
}
@@ -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;
@@ -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);
}
@@ -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));
}
@@ -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;
@@ -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 -
@@ -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));
@@ -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. */
}
@@ -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 ----- */
@@ -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");
/** \} */
@@ -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",
+32 -1
View File
@@ -529,6 +529,7 @@ class Texture : NonCopyable {
GPUTexture *stencil_view_ = nullptr;
Vector<GPUTexture *, 0> mip_views_;
Vector<GPUTexture *, 0> 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();
}
@@ -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);
}