diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index e1aad7d4280..dc16818d22b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -429,6 +429,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_transparency_lib.glsl engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl + engines/eevee_next/shaders/eevee_deferred_light_frag.glsl engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl @@ -448,6 +449,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl engines/eevee_next/shaders/eevee_film_frag.glsl engines/eevee_next/shaders/eevee_film_lib.glsl + engines/eevee_next/shaders/eevee_gbuffer_lib.glsl engines/eevee_next/shaders/eevee_geom_curves_vert.glsl engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index bc778dc874d..c54d537db61 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -105,6 +105,9 @@ #define RBUFS_AOV_COLOR_SLOT 5 #define RBUFS_AOV_VALUE_SLOT 6 #define RBUFS_CRYPTOMATTE_SLOT 7 +/* G-buffer reuses render passes slots. */ +#define GBUF_CLOSURE_SLOT RBUFS_LIGHT_SLOT +#define GBUF_COLOR_SLOT RBUFS_DIFF_COLOR_SLOT /* Uniform Buffers. */ /* Only during prepass. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index beab3bdb196..d49ddd7866e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -649,7 +649,7 @@ void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx) draw::View drw_view("MainView", view); - DRW_manager_get()->submit(accumulate_ps_, drw_view); + inst_.manager->submit(accumulate_ps_, drw_view); combined_tx_.swap(); weight_tx_.swap(); diff --git a/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh new file mode 100644 index 00000000000..74a17e317f2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_gbuffer.hh @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + * + * Gbuffer layout used for deferred shading pipeline. + */ + +#pragma once + +#include "DRW_render.h" + +#include "eevee_material.hh" +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/** + * Fullscreen textures containing geometric and surface data. + * Used by deferred shading passes. Only one gbuffer 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 + * + * 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. + */ +struct GBuffer { + /* TODO(fclem): Use texture from pool once they support texture array. */ + Texture closure_tx = {"GbufferClosure"}; + Texture color_tx = {"GbufferColor"}; + + void acquire(int2 extent, eClosureBits closure_bits_) + { + const bool use_sss = (closure_bits_ & CLOSURE_SSS) != 0; + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + closure_tx.ensure_2d_array(GPU_RGBA16, extent, use_sss ? 3 : 2, usage); + color_tx.ensure_2d_array(GPU_RGB10_A2, extent, 2, usage); + } + + void release() + { + /* TODO(fclem): Use texture from pool once they support texture array. */ + // closure_tx.release(); + // color_tx.release(); + } +}; + +} // namespace blender::eevee \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 491190334e1..b0a40d27982 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -105,6 +105,7 @@ void Instance::begin_sync() velocity.begin_sync(); /* NOTE: Also syncs camera. */ lights.begin_sync(); shadows.begin_sync(); + pipelines.begin_sync(); cryptomatte.begin_sync(); gpencil_engine_enabled = false; @@ -114,7 +115,6 @@ void Instance::begin_sync() depth_of_field.sync(); motion_blur.sync(); hiz_buffer.sync(); - pipelines.sync(); main_view.sync(); world.sync(); film.sync(); @@ -206,6 +206,7 @@ void Instance::end_sync() sampling.end_sync(); film.end_sync(); cryptomatte.end_sync(); + pipelines.end_sync(); } void Instance::render_sync() diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 6bb54a37a2c..8e7855961e4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -19,6 +19,7 @@ #include "eevee_cryptomatte.hh" #include "eevee_depth_of_field.hh" #include "eevee_film.hh" +#include "eevee_gbuffer.hh" #include "eevee_hizbuffer.hh" #include "eevee_irradiance_cache.hh" #include "eevee_light.hh" @@ -54,6 +55,7 @@ class Instance { MotionBlurModule motion_blur; DepthOfField depth_of_field; Cryptomatte cryptomatte; + GBuffer gbuffer; HiZBuffer hiz_buffer; Sampling sampling; Camera camera; diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 43c91dc32d5..a210681517a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -193,11 +193,6 @@ MaterialPass MaterialModule::material_pass_get(Object *ob, inst_.sampling.reset(); } - if ((pipeline_type == MAT_PIPE_DEFERRED) && - GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) { - pipeline_type = MAT_PIPE_FORWARD; - } - if (ELEM(pipeline_type, MAT_PIPE_FORWARD, MAT_PIPE_FORWARD_PREPASS, @@ -240,10 +235,6 @@ Material &MaterialModule::material_sync(Object *ob, (has_motion ? MAT_PIPE_DEFERRED_PREPASS_VELOCITY : MAT_PIPE_DEFERRED_PREPASS); - /* TEST until we have deferred pipeline up and running. */ - surface_pipe = MAT_PIPE_FORWARD; - prepass_pipe = has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : MAT_PIPE_FORWARD_PREPASS; - MaterialKey material_key(blender_mat, geometry_type, surface_pipe); Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() { diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 94ce44522ba..8b6f0ccfdce 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -278,4 +278,229 @@ void ForwardPipeline::render(View &view, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Deferred Layer + * \{ */ + +void DeferredLayer::begin_sync() +{ + { + prepass_ps_.init(); + { + /* Common resources. */ + + /* Textures. */ + prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + /* Uniform Buf. */ + prepass_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); + + inst_.velocity.bind_resources(&prepass_ps_); + inst_.sampling.bind_resources(&prepass_ps_); + } + + DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + DRWState state_depth_color = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | + DRW_STATE_WRITE_COLOR; + + prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static"); + prepass_double_sided_static_ps_->state_set(state_depth_only); + + prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static"); + prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK); + + prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving"); + prepass_double_sided_moving_ps_->state_set(state_depth_color); + + prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving"); + prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK); + } + { + gbuffer_ps_.init(); + gbuffer_ps_.clear_stencil(0x00u); + gbuffer_ps_.state_stencil(0x01u, 0x01u, 0x01u); + + { + /* 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); + /* RenderPasses. */ + gbuffer_ps_.bind_image(RBUFS_NORMAL_SLOT, &inst_.render_buffers.normal_tx); + /* TODO(fclem): Pack all render pass into the same texture. */ + // gbuffer_ps_.bind_image(RBUFS_DIFF_COLOR_SLOT, &inst_.render_buffers.diffuse_color_tx); + gbuffer_ps_.bind_image(RBUFS_SPEC_COLOR_SLOT, &inst_.render_buffers.specular_color_tx); + gbuffer_ps_.bind_image(RBUFS_EMISSION_SLOT, &inst_.render_buffers.emission_tx); + /* AOVs. */ + gbuffer_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx); + gbuffer_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx); + /* Cryptomatte. */ + gbuffer_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx); + /* Storage Buf. */ + gbuffer_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info); + /* Textures. */ + gbuffer_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + /* Uniform Buf. */ + gbuffer_ps_.bind_ubo(CAMERA_BUF_SLOT, inst_.camera.ubo_get()); + + inst_.sampling.bind_resources(&gbuffer_ps_); + inst_.cryptomatte.bind_resources(&gbuffer_ps_); + } + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_EQUAL | + DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; + + gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided"); + gbuffer_double_sided_ps_->state_set(state); + + gbuffer_single_sided_ps_ = &gbuffer_ps_.sub("SingleSided"); + gbuffer_single_sided_ps_->state_set(state | DRW_STATE_CULL_BACK); + } +} + +void DeferredLayer::end_sync() +{ + /* Use stencil test to reject pixel not written by this layer. */ + /* WORKAROUND: Stencil write is only here to avoid rasterizer discard. */ + DRWState state = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_EQUAL; + /* Allow output to combined pass for the last pass. */ + DRWState state_write_color = state | DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; + + if (closure_bits_ & (CLOSURE_DIFFUSE | CLOSURE_REFLECTION)) { + const bool is_last_eval_pass = true; + + eval_light_ps_.init(); + eval_light_ps_.state_set(is_last_eval_pass ? state_write_color : state); + eval_light_ps_.state_stencil(0x00u, 0x01u, 0xFFu); + eval_light_ps_.shader_set(inst_.shaders.static_shader_get(DEFERRED_LIGHT)); + eval_light_ps_.bind_image("out_diffuse_light_img", &diffuse_light_tx_); + eval_light_ps_.bind_image("out_specular_light_img", &specular_light_tx_); + eval_light_ps_.bind_texture("gbuffer_closure_tx", &inst_.gbuffer.closure_tx); + eval_light_ps_.bind_texture("gbuffer_color_tx", &inst_.gbuffer.color_tx); + eval_light_ps_.push_constant("is_last_eval_pass", is_last_eval_pass); + eval_light_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx); + eval_light_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx); + + inst_.lights.bind_resources(&eval_light_ps_); + inst_.shadows.bind_resources(&eval_light_ps_); + inst_.sampling.bind_resources(&eval_light_ps_); + inst_.hiz_buffer.bind_resources(&eval_light_ps_); + + eval_light_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); + eval_light_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3); + } +} + +PassMain::Sub *DeferredLayer::prepass_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion) +{ + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + (has_motion ? prepass_single_sided_moving_ps_ : + prepass_single_sided_static_ps_) : + (has_motion ? prepass_double_sided_moving_ps_ : + prepass_double_sided_static_ps_); + + return &pass->sub(GPU_material_get_name(gpumat)); +} + +PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + closure_bits_ |= shader_closure_bits_from_flag(gpumat); + + PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + gbuffer_single_sided_ps_ : + gbuffer_double_sided_ps_; + return &pass->sub(GPU_material_get_name(gpumat)); +} + +void DeferredLayer::render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + int2 extent) +{ + + GPU_framebuffer_bind(prepass_fb); + inst_.manager->submit(prepass_ps_, view); + + inst_.hiz_buffer.set_dirty(); + inst_.shadows.set_view(view); + + inst_.gbuffer.acquire(extent, closure_bits_); + + GPU_framebuffer_bind(combined_fb); + inst_.manager->submit(gbuffer_ps_, view); + + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE; + diffuse_light_tx_.acquire(extent, GPU_RGBA16F, usage); + specular_light_tx_.acquire(extent, GPU_RGBA16F, usage); + diffuse_light_tx_.clear(float4(0.0f)); + specular_light_tx_.clear(float4(0.0f)); + + inst_.manager->submit(eval_light_ps_, view); + + diffuse_light_tx_.release(); + specular_light_tx_.release(); + + inst_.gbuffer.release(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deferred Pipeline + * + * Closure data are written to intermediate buffer allowing screen space processing. + * \{ */ + +void DeferredPipeline::begin_sync() +{ + opaque_layer_.begin_sync(); + refraction_layer_.begin_sync(); +} + +void DeferredPipeline::end_sync() +{ + opaque_layer_.end_sync(); + refraction_layer_.end_sync(); +} + +PassMain::Sub *DeferredPipeline::prepass_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion) +{ + if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) { + return refraction_layer_.prepass_add(blender_mat, gpumat, has_motion); + } + else { + return opaque_layer_.prepass_add(blender_mat, gpumat, has_motion); + } +} + +PassMain::Sub *DeferredPipeline::material_add(::Material *blender_mat, GPUMaterial *gpumat) +{ + if (blender_mat->blend_flag & MA_BL_SS_REFRACTION) { + return refraction_layer_.material_add(blender_mat, gpumat); + } + else { + return opaque_layer_.material_add(blender_mat, gpumat); + } +} + +void DeferredPipeline::render(View &view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + int2 extent) +{ + DRW_stats_group_start("Deferred.Opaque"); + opaque_layer_.render(view, prepass_fb, combined_fb, extent); + DRW_stats_group_end(); + + DRW_stats_group_start("Deferred.Refract"); + refraction_layer_.render(view, prepass_fb, combined_fb, extent); + DRW_stats_group_end(); +} + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index a437ca8c522..d390fad4775 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -113,6 +113,77 @@ class ForwardPipeline { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Deferred lighting. + * \{ */ + +class DeferredLayer { + private: + Instance &inst_; + + PassMain prepass_ps_ = {"Prepass"}; + PassMain::Sub *prepass_single_sided_static_ps_ = nullptr; + PassMain::Sub *prepass_single_sided_moving_ps_ = nullptr; + PassMain::Sub *prepass_double_sided_static_ps_ = nullptr; + PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr; + + PassMain gbuffer_ps_ = {"Shading"}; + PassMain::Sub *gbuffer_single_sided_ps_ = nullptr; + PassMain::Sub *gbuffer_double_sided_ps_ = nullptr; + + PassSimple eval_light_ps_ = {"EvalLights"}; + + /* Closures bits from the materials in this pass. */ + eClosureBits closure_bits_; + + /** + * Accumulation textures for all stages of lighting evaluation (Light, SSR, SSSS, SSGI ...). + * These are split and separate from the main radiance buffer in order to accumulate light for + * the render passes and avoid too much bandwidth waste. Otherwise, we would have to load the + * BSDF color and do additive blending for each of the lighting step. + * + * NOTE: Not to be confused with the render passes. + */ + TextureFromPool diffuse_light_tx_ = {"diffuse_light_accum_tx"}; + TextureFromPool specular_light_tx_ = {"specular_light_accum_tx"}; + + public: + DeferredLayer(Instance &inst) : inst_(inst){}; + + void begin_sync(); + void end_sync(); + + PassMain::Sub *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion); + PassMain::Sub *material_add(::Material *blender_mat, GPUMaterial *gpumat); + + void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); +}; + +class DeferredPipeline { + private: + Instance &inst_; + + /* Gbuffer filling passes. We could have an arbitrary number of them but for now we just have + * a hardcoded number of them. */ + DeferredLayer opaque_layer_; + DeferredLayer refraction_layer_; + DeferredLayer volumetric_layer_; + + public: + DeferredPipeline(Instance &inst) + : inst_(inst), opaque_layer_(inst), refraction_layer_(inst), volumetric_layer_(inst){}; + + void begin_sync(); + void end_sync(); + + PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat, bool has_motion); + PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat); + + void render(View &view, Framebuffer &prepass_fb, Framebuffer &combined_fb, int2 extent); +}; + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Utility texture * @@ -197,22 +268,25 @@ class UtilityTexture : public Texture { class PipelineModule { public: WorldPipeline world; - // DeferredPipeline deferred; + DeferredPipeline deferred; ForwardPipeline forward; ShadowPipeline shadow; - // VelocityPipeline velocity; UtilityTexture utility_tx; public: - PipelineModule(Instance &inst) : world(inst), forward(inst), shadow(inst){}; + PipelineModule(Instance &inst) : world(inst), deferred(inst), forward(inst), shadow(inst){}; - void sync() + void begin_sync() { - // deferred.sync(); + deferred.begin_sync(); forward.sync(); shadow.sync(); - // velocity.sync(); + } + + void end_sync() + { + deferred.end_sync(); } PassMain::Sub *material_add(Object *ob, @@ -222,7 +296,7 @@ class PipelineModule { { switch (pipeline_type) { case MAT_PIPE_DEFERRED_PREPASS: - // return deferred.prepass_add(blender_mat, gpumat, false); + return deferred.prepass_add(blender_mat, gpumat, false); case MAT_PIPE_FORWARD_PREPASS: if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { return forward.prepass_transparent_add(ob, blender_mat, gpumat); @@ -230,7 +304,7 @@ class PipelineModule { return forward.prepass_opaque_add(blender_mat, gpumat, false); case MAT_PIPE_DEFERRED_PREPASS_VELOCITY: - // return deferred.prepass_add(blender_mat, gpumat, true); + return deferred.prepass_add(blender_mat, gpumat, true); case MAT_PIPE_FORWARD_PREPASS_VELOCITY: if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { return forward.prepass_transparent_add(ob, blender_mat, gpumat); @@ -238,7 +312,7 @@ class PipelineModule { return forward.prepass_opaque_add(blender_mat, gpumat, true); case MAT_PIPE_DEFERRED: - // return deferred.material_add(blender_mat, gpumat); + return deferred.material_add(blender_mat, gpumat); case MAT_PIPE_FORWARD: if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { return forward.material_transparent_add(ob, blender_mat, gpumat); diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 4d9ac75687c..116d6004642 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -86,6 +86,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_film_comp"; case FILM_CRYPTOMATTE_POST: return "eevee_film_cryptomatte_post"; + case DEFERRED_LIGHT: + return "eevee_deferred_light"; case HIZ_DEBUG: return "eevee_hiz_debug"; case HIZ_UPDATE: @@ -241,6 +243,11 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu } } + /* WORKAROUND: Needed because node_tree isn't present in test shaders. */ + if (pipeline_type == MAT_PIPE_DEFERRED) { + info.define("MAT_RENDER_PASS_SUPPORT"); + } + if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) { info.define("MAT_TRANSPARENT"); /* Transparent material do not have any velocity specific pipeline. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 173dc7bfa86..d987105e3fc 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -30,6 +30,8 @@ enum eShaderType { FILM_COMP, FILM_CRYPTOMATTE_POST, + DEFERRED_LIGHT, + DEBUG_SURFELS, DOF_BOKEH_LUT, diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 8037af78db1..d2d8f80feb7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -123,8 +123,7 @@ void ShadingView::render() /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ inst_.lights.set_view(render_view_new_, extent_); - // inst_.pipelines.deferred.render( - // render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_); + inst_.pipelines.deferred.render(render_view_new_, prepass_fb_, combined_fb_, extent_); // inst_.lightprobes.draw_cache_display(); 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 new file mode 100644 index 00000000000..80dcaaf67cb --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_deferred_light_frag.glsl @@ -0,0 +1,97 @@ + +/** + * Compute light objects lighting contribution using Gbuffer data. + * + * Output light either directly to the radiance buffers or to temporary radiance accumulation + * buffer that will be processed by other deferred lighting passes. + */ + +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + + float depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r; + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + + /* TODO(fclem): High precision derivative. */ + vec3 Ng = safe_normalize(cross(dFdx(P), dFdy(P))); + vec3 V = cameraVec(P); + float vP_z = dot(cameraForward, P) - dot(cameraForward, cameraPos); + + vec4 gbuffer_0_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 0), 0); + vec4 gbuffer_1_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 1), 0); + + ClosureReflection reflection_data; + reflection_data.N = gbuffer_normal_unpack(gbuffer_0_packed.xy); + reflection_data.roughness = gbuffer_0_packed.z; + + ClosureDiffuse diffuse_data; + diffuse_data.N = gbuffer_normal_unpack(gbuffer_1_packed.xy); + /* These are only set for SSS case. */ + diffuse_data.sss_radius = vec3(0.0); + diffuse_data.sss_id = 0u; + float thickness = 0.0; + + bool is_refraction = gbuffer_is_refraction(gbuffer_1_packed); + if (is_refraction) { + /* Still evaluate the diffuse light so that dithered SSS / Refraction combination still + * produces a complete diffuse light buffer that will be correctly convolved by the SSSS. + * The refraction pixels will just set the diffuse radiance to 0. */ + } + else if (false /* TODO */) { + vec4 gbuffer_2_packed = texelFetch(gbuffer_closure_tx, ivec3(texel, 2), 0); + diffuse_data.sss_radius = gbuffer_sss_radii_unpack(gbuffer_2_packed.xyz); + diffuse_data.sss_id = gbuffer_object_id_unorm16_unpack(gbuffer_2_packed.w); + thickness = gbuffer_thickness_pack(gbuffer_1_packed.z); + } + + vec3 diffuse_light = vec3(0.0); + vec3 reflection_light = vec3(0.0); + + light_eval( + diffuse_data, reflection_data, P, Ng, V, vP_z, thickness, diffuse_light, reflection_light); + + if (is_last_eval_pass) { + /* Apply color and output lighting to render-passes. */ + vec4 color_0_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 0), 0); + vec4 color_1_packed = texelFetch(gbuffer_color_tx, ivec3(texel, 1), 0); + + reflection_data.color = gbuffer_color_unpack(color_0_packed); + diffuse_data.color = gbuffer_color_unpack(color_1_packed); + + if (is_refraction) { + diffuse_data.color = vec3(0.0); + } + + reflection_light *= reflection_data.color; + diffuse_light *= diffuse_data.color; + /* Add radiance to light pass. */ + imageStore( + rp_light_img, ivec3(texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(diffuse_light, 1.0)); + imageStore( + rp_light_img, ivec3(texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(reflection_light, 1.0)); + /* Add radiance to combined pass. */ + out_radiance = vec4(diffuse_light + reflection_light, 0.0); + out_transmittance = vec4(1.0); + } + else { + /* Store lighting for next deferred pass. */ + + /* Output diffuse light along with object ID for sub-surface screen space processing. */ + vec4 diffuse_radiance; + diffuse_radiance.xyz = diffuse_light; + diffuse_radiance.w = gbuffer_object_id_f16_pack(diffuse_data.sss_id); + imageStore(out_diffuse_light_img, texel, diffuse_radiance); + + imageStore(out_specular_light_img, texel, vec4(reflection_light, 0.0)); + + /* Final radiance will be amended by the last pass. + * This should do nothing as color write should be disabled in this case. */ + out_radiance = vec4(0.0); + out_transmittance = vec4(0.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 new file mode 100644 index 00000000000..8156166f42f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_gbuffer_lib.glsl @@ -0,0 +1,106 @@ + +/** + * G-buffer: Packing and upacking of G-buffer data. + * + * See #GBuffer for a breakdown of the G-buffer layout. + */ + +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) + +vec2 gbuffer_normal_pack(vec3 N) +{ + N /= length_manhattan(N); + N.xy = (N.z >= 0.0) ? N.xy : ((1.0 - abs(N.yx)) * sign(N.xy)); + N.xy = N.xy * 0.5 + 0.5; + return N.xy; +} + +vec3 gbuffer_normal_unpack(vec2 N_packed) +{ + N_packed = N_packed * 2.0 - 1.0; + vec3 N = vec3(N_packed.x, N_packed.y, 1.0 - abs(N_packed.x) - abs(N_packed.y)); + float t = clamp(-N.z, 0.0, 1.0); + N.x += (N.x >= 0.0) ? -t : t; + N.y += (N.y >= 0.0) ? -t : t; + return normalize(N); +} + +float gbuffer_ior_pack(float ior) +{ + return (ior > 1.0) ? (1.0 - 0.5 / ior) : (0.5 * ior); +} + +float gbuffer_ior_unpack(float ior_packed) +{ + return (ior_packed > 0.5) ? (-1.0 / (ior_packed * 2.0 + 2.0)) : (2.0 * ior_packed); +} + +float gbuffer_thickness_pack(float thickness) +{ + /* TODO(fclem): Something better. */ + return gbuffer_ior_pack(thickness); +} + +float gbuffer_thickness_unpack(float thickness_packed) +{ + /* TODO(fclem): Something better. */ + return gbuffer_ior_unpack(thickness_packed); +} + +vec3 gbuffer_sss_radii_pack(vec3 sss_radii) +{ + /* TODO(fclem): Something better. */ + return vec3( + gbuffer_ior_pack(sss_radii.x), gbuffer_ior_pack(sss_radii.y), gbuffer_ior_pack(sss_radii.z)); +} + +vec3 gbuffer_sss_radii_unpack(vec3 sss_radii_packed) +{ + /* TODO(fclem): Something better. */ + return vec3(gbuffer_ior_unpack(sss_radii_packed.x), + gbuffer_ior_unpack(sss_radii_packed.y), + gbuffer_ior_unpack(sss_radii_packed.z)); +} + +vec4 gbuffer_color_pack(vec3 color) +{ + float max_comp = max(color.x, max(color.y, color.z)); + /* Store 2bit exponent inside Alpha. Allows values up to 8 with some color degradation. + * Above 8, the result will be clampped when writing the data to the output buffer. */ + float exponent = (max_comp > 1) ? ((max_comp > 2) ? ((max_comp > 4) ? 3.0 : 2.0) : 1.0) : 0.0; + /* TODO(fclem): Could try dithering to avoid banding artifacts on higher exponents. */ + return vec4(color / exp2(exponent), exponent / 3.0); +} + +vec3 gbuffer_color_unpack(vec4 color_packed) +{ + float exponent = color_packed.a * 3.0; + return color_packed.rgb * exp2(exponent); +} + +float gbuffer_object_id_unorm16_pack(uint object_id) +{ + return float(object_id & 0xFFFFu) / float(0xFFFF); +} + +uint gbuffer_object_id_unorm16_unpack(float object_id_packed) +{ + return uint(object_id_packed * float(0xFFFF)); +} + +float gbuffer_object_id_f16_pack(uint object_id) +{ + /* TODO(fclem): Make use of all the 16 bits in a half float. + * This here only correctly represent values up to 1024. */ + return float(object_id); +} + +uint gbuffer_object_id_f16_unpack(float object_id_packed) +{ + return uint(object_id_packed); +} + +bool gbuffer_is_refraction(vec4 gbuffer) +{ + return gbuffer.w < 1.0; +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 965780d9bcf..db38baab6a4 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -29,14 +29,16 @@ bool closure_select(float weight, inout float total_weight, inout float r) float x = weight / total_weight; bool chosen = (r < x); /* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within - * that interval, so you remapping to [0,1] again to explore this space of probability. */ + * that interval, so remapping to [0,1] again to explore this space of probability. */ r = (chosen) ? (r / x) : ((r - x) / (1.0 - x)); return chosen; } #define SELECT_CLOSURE(destination, random, candidate) \ if (closure_select(candidate.weight, destination.weight, random)) { \ + float tmp = destination.weight; \ destination = candidate; \ + destination.weight = tmp; \ } float g_closure_rand; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl index dd946ffb637..7bc51513da1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_frag.glsl @@ -82,7 +82,7 @@ void main() { vec2 screen_uv = gl_FragCoord.xy / vec2(fb_resolution); - float opaque_depth = texelFetch(hiz_tx, int2(gl_FragCoord.xy), fb_lod).r; + float opaque_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), fb_lod).r; vec3 ws_opaque = get_world_space_from_depth(screen_uv, opaque_depth); vec3 ws_near_plane = get_world_space_from_depth(screen_uv, 0); 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 7848c4a0611..d84d0a030e2 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 @@ -3,17 +3,136 @@ * Deferred lighting evaluation: Lighting is evaluated in a separate pass. * * Outputs shading parameter per pixel using a randomized set of BSDFs. - **/ + * Some render-pass are written during this pass. + */ +#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) + +vec4 closure_to_rgba(Closure cl) +{ + vec4 out_color; + out_color.rgb = g_emission; + out_color.a = saturate(1.0 - avg(g_transmittance)); + + /* Reset for the next closure tree. */ + closure_weights_reset(); + + return out_color; +} void main() { init_globals(); + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + + fragment_displacement(); + nodetree_surface(); + + g_holdout = saturate(g_holdout); + + out_transmittance = vec4(1.0 - g_holdout); + float transmittance_mono = saturate(avg(g_transmittance)); + + float thickness = nodetree_thickness(); + + g_diffuse_data.color *= g_diffuse_data.weight; + g_reflection_data.color *= g_reflection_data.weight; + g_refraction_data.color *= g_refraction_data.weight; + + /* TODO(fclem): This feels way too complex for what is it. */ + bool has_any_bsdf_weight = g_diffuse_data.weight != 0.0 || g_reflection_data.weight != 0.0 || + g_refraction_data.weight != 0.0; + vec3 out_normal = has_any_bsdf_weight ? vec3(0.0) : g_data.N; + out_normal += g_diffuse_data.N * g_diffuse_data.weight; + out_normal += g_reflection_data.N * g_reflection_data.weight; + out_normal += g_refraction_data.N * g_refraction_data.weight; + out_normal = safe_normalize(out_normal); + + vec3 specular_color = g_reflection_data.color + g_refraction_data.color; + + /* ----- Render Passes output ----- */ + + ivec2 out_texel = ivec2(gl_FragCoord.xy); +#ifdef MAT_RENDER_PASS_SUPPORT /* Needed because node_tree isn't present in test shaders. */ + /* Some render pass can be written during the gbuffer pass. Light passes are written later. */ + vec4 cryptomatte_output = vec4(cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0); + imageStore(rp_cryptomatte_img, out_texel, cryptomatte_output); + imageStore(rp_normal_img, out_texel, vec4(out_normal, 1.0)); + /* TODO(fclem): For now, just don't do anything. In the future all render passes should be in an + * array texture and have a UBO with indirection to the correct layer. */ + // imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0)); + imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0)); + imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0)); +#endif + + /* ----- GBuffer output ----- */ + + if (true) { + /* Reflection. */ + vec4 out_reflect = vec4(gbuffer_normal_pack(g_reflection_data.N), + g_reflection_data.roughness, + g_reflection_data.roughness); + imageStore(out_gbuff_closure_img, ivec3(out_texel, 0), out_reflect); + + vec4 color = gbuffer_color_pack(g_reflection_data.color); + imageStore(out_gbuff_color_img, ivec3(out_texel, 0), color); + } + + /* TODO(fclem) other RNG. */ + float refract_rand = fract(g_closure_rand * 6.1803398875); + float combined_weight = g_refraction_data.weight + g_diffuse_data.weight; + bool output_refraction = combined_weight > 0.0 && + (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); + /* Clamp to just bellow 1 to be able to distinguish between refraction and diffuse. + * Ceiling value is chosen by the storage format (16bit UNORM). */ + closure.w = min(closure.w, float(0xFFFFu - 1u) / float(0xFFFFu)); + imageStore(out_gbuff_closure_img, ivec3(out_texel, 1), closure); + + vec4 color = gbuffer_color_pack(g_refraction_data.color); + imageStore(out_gbuff_color_img, ivec3(out_texel, 1), color); + } + else { + /* Diffuse. */ + vec4 closure; + closure.xy = gbuffer_normal_pack(g_diffuse_data.N); + closure.z = gbuffer_thickness_pack(thickness); + /* Used to detect the refraction case. Could be used for roughness. */ + closure.w = 1.0; + imageStore(out_gbuff_closure_img, ivec3(out_texel, 1), closure); + + vec4 color = gbuffer_color_pack(g_diffuse_data.color); + imageStore(out_gbuff_color_img, ivec3(out_texel, 1), color); + } + + if (true) { + /* SubSurface Scattering. */ + vec4 closure; + closure.xyz = gbuffer_sss_radii_pack(g_diffuse_data.sss_radius); + closure.w = gbuffer_object_id_unorm16_pack(g_diffuse_data.sss_id); + imageStore(out_gbuff_closure_img, ivec3(out_texel, 2), closure); + } + + /* ----- Radiance output ----- */ + + /* Only output emission during the gbuffer pass. */ + out_radiance = vec4(g_emission, 0.0); + out_radiance.rgb *= 1.0 - g_holdout; + + out_transmittance.rgb = g_transmittance; + out_transmittance.a = saturate(avg(g_transmittance)); } 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 new file mode 100644 index 00000000000..f5bc967e5b6 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +#define image_out(slot, qualifier, format, name) \ + image(slot, format, qualifier, ImageType::FLOAT_2D, name, Frequency::PASS) +#define image_array_out(slot, qualifier, format, name) \ + image(slot, format, qualifier, ImageType::FLOAT_2D_ARRAY, name, Frequency::PASS) + +/** + * Specific deferred pass accumulate the computed lighting to either: + * - a split diffuse / specular temporary light buffer. + * or to + * - the combined pass & the light render-pass (if needed). + * + * This is in order to minimize the number of blending step. + */ +GPU_SHADER_CREATE_INFO(eevee_deferred_base) + /* Early fragment test is needed to avoid processing fragments without correct GBuffer data. */ + .early_fragment_test(true) + /* Select which output to write to. */ + .push_constant(Type::BOOL, "is_last_eval_pass") + /* Combined pass output. */ + .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) + .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1) + /* Light pass output. */ + .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img") + /* Chaining to next pass. */ + .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "out_diffuse_light_img") + .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "out_specular_light_img"); + +GPU_SHADER_CREATE_INFO(eevee_deferred_light) + .fragment_source("eevee_deferred_light_frag.glsl") + .sampler(0, ImageType::FLOAT_2D_ARRAY, "gbuffer_closure_tx") + .sampler(1, ImageType::FLOAT_2D_ARRAY, "gbuffer_color_tx") + .additional_info("eevee_shared", + "eevee_utility_texture", + "eevee_light_data", + "eevee_shadow_data", + "eevee_deferred_base", + "eevee_hiz_data", + "draw_view", + "draw_fullscreen") + .do_static_compilation(true); + +#undef image_array_out 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 0ef16bf3522..f16113a5efc 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 @@ -88,11 +88,11 @@ GPU_SHADER_CREATE_INFO(eevee_aov_out) GPU_SHADER_CREATE_INFO(eevee_render_pass_out) .define("MAT_RENDER_PASS_SUPPORT") - .image_out(RBUFS_NORMAL_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img") - .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img") - .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img") - .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") - .image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img"); + .image_out(RBUFS_NORMAL_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_normal_img") + .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_light_img") + .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_diffuse_color_img") + .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_specular_color_img") + .image_out(RBUFS_EMISSION_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_emission_img"); GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out) .storage_buf(CRYPTOMATTE_BUF_SLOT, Qualifier::READ, "vec2", "cryptomatte_object_buf[]") @@ -101,23 +101,29 @@ GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out) GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) /* NOTE: This removes the possibility of using gl_FragDepth. */ - // .early_fragment_test(true) - /* Direct output. */ + .early_fragment_test(true) + /* Direct output. (Emissive, Holdout) */ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) .fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1) - /* Gbuffer. */ - // .image_out(0, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_color") - // .image_out(1, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_transmit_data") - // .image_out(2, Qualifier::WRITE, GPU_RGBA16F, "gbuff_transmit_normal") - // .image_out(3, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_reflection_color") - // .image_out(4, Qualifier::WRITE, GPU_RGBA16F, "gbuff_reflection_normal") - // .image_out(5, Qualifier::WRITE, GPU_R11F_G11F_B10F, "gbuff_emission") - /* Render-passes. */ - // .image_out(6, Qualifier::READ_WRITE, GPU_RGBA16F, "rpass_volume_light") + /* 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_gbuff_closure_img") + .image_array_out(GBUF_COLOR_SLOT, Qualifier::WRITE, GPU_RGB10_A2, "out_gbuff_color_img") + /* Render-passes need to be declared manually to avoid overlap with the G-buffer which reuse + * some of binding points. */ + .image_out(RBUFS_NORMAL_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_normal_img") + // .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_light_img") + /* TODO(fclem): Merge all render-pass into the same texture array. */ + // .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_diffuse_color_img") + .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_specular_color_img") + .image_out(RBUFS_EMISSION_SLOT, Qualifier::WRITE, GPU_RGBA16F, "rp_emission_img") .fragment_source("eevee_surf_deferred_frag.glsl") .additional_info("eevee_camera", "eevee_utility_texture", "eevee_sampling_data", + /* Added manually to avoid overlap. */ + // "eevee_render_pass_out", + "eevee_cryptomatte_out", "eevee_aov_out"); GPU_SHADER_CREATE_INFO(eevee_surf_forward) diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index dc155399425..d572cc75b27 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -206,7 +206,8 @@ void DRW_texture_pool_reset(DRWTexturePool *pool) } } - BLI_assert(pool->tmp_tex_acquired.is_empty()); + BLI_assert_msg(pool->tmp_tex_acquired.is_empty(), + "Missing a TextureFromPool.release() before end of draw."); for (GPUTexture *tmp_tex : pool->tmp_tex_pruned) { GPU_texture_free(tmp_tex); } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index bfc0a1b3c7a..329cb211c3f 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -603,6 +603,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_SHADER_CREATE_INFOS ../draw/engines/basic/shaders/infos/basic_depth_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_deferred_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index ca9c42f2027..ddb014cc045 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -1,18 +1,23 @@ /* Float Math */ +/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */ +#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL + float safe_divide(float a, float b) { return (b != 0.0) ? a / b : 0.0; } +#endif + /* fmod function compatible with OSL (copy from OSL/dual.h) */ float compatible_fmod(float a, float b) { - if (b != 0.0f) { + if (b != 0.0) { int N = int(a / b); return a - N * b; } - return 0.0f; + return 0.0; } float compatible_pow(float x, float y) @@ -59,11 +64,16 @@ vec3 wrap(vec3 a, vec3 b, vec3 c) return vec3(wrap(a.x, b.x, c.x), wrap(a.y, b.y, c.y), wrap(a.z, b.z, c.z)); } +/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */ +#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL + float hypot(float x, float y) { return sqrt(x * x + y * y); } +#endif + int floor_to_int(float x) { return int(floor(x)); @@ -76,6 +86,9 @@ int quick_floor(float x) /* Vector Math */ +/* WORKAROUND: To be removed once we port all code to use gpu_shader_math_base_lib.glsl. */ +#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL + vec2 safe_divide(vec2 a, vec2 b) { return vec2(safe_divide(a.x, b.x), safe_divide(a.y, b.y)); @@ -107,6 +120,8 @@ vec4 safe_divide(vec4 a, float b) return (b != 0.0) ? a / b : vec4(0.0); } +#endif + vec3 compatible_fmod(vec3 a, vec3 b) { return vec3(compatible_fmod(a.x, b.x), compatible_fmod(a.y, b.y), compatible_fmod(a.z, b.z));