summaryrefslogtreecommitdiff
path: root/servers/rendering/renderer_rd/effects
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering/renderer_rd/effects')
-rw-r--r--servers/rendering/renderer_rd/effects/bokeh_dof.cpp94
-rw-r--r--servers/rendering/renderer_rd/effects/bokeh_dof.h11
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp137
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.h46
-rw-r--r--servers/rendering/renderer_rd/effects/fsr.cpp128
-rw-r--r--servers/rendering/renderer_rd/effects/fsr.h72
-rw-r--r--servers/rendering/renderer_rd/effects/resolve.h2
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.cpp1876
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.h552
-rw-r--r--servers/rendering/renderer_rd/effects/taa.cpp138
-rw-r--r--servers/rendering/renderer_rd/effects/taa.h68
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.cpp4
-rw-r--r--servers/rendering/renderer_rd/effects/tone_mapper.h6
-rw-r--r--servers/rendering/renderer_rd/effects/vrs.cpp58
-rw-r--r--servers/rendering/renderer_rd/effects/vrs.h4
15 files changed, 3092 insertions, 104 deletions
diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
index cc7441776d..27850695b0 100644
--- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
+++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
@@ -33,6 +33,8 @@
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+#include "servers/rendering/rendering_server_default.h"
+#include "servers/rendering/storage/camera_attributes_storage.h"
using namespace RendererRD;
@@ -84,7 +86,7 @@ BokehDOF::~BokehDOF() {
}
}
-void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of bokeh depth of field with the mobile renderer.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
@@ -92,22 +94,39 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
+ bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes);
+ float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes);
+ float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes);
+ bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes);
+ float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes);
+ float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes);
+ float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius.
+
+ bool use_jitter = RSG::camera_attributes->camera_attributes_get_dof_blur_use_jitter();
+ RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape();
+ RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality();
+
// setup our push constant
memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
- bokeh.push_constant.blur_far_active = p_dof_far;
- bokeh.push_constant.blur_far_begin = p_dof_far_begin;
- bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
-
- bokeh.push_constant.blur_near_active = p_dof_near;
- bokeh.push_constant.blur_near_begin = p_dof_near_begin;
- bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size);
- bokeh.push_constant.use_jitter = p_use_jitter;
+ bokeh.push_constant.blur_far_active = dof_far;
+ bokeh.push_constant.blur_far_begin = dof_far_begin;
+ bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; // Only used with non-physically-based.
+ bokeh.push_constant.use_physical_far = dof_far_size < 0.0;
+ bokeh.push_constant.blur_size_far = bokeh_size; // Only used with physically-based.
+
+ bokeh.push_constant.blur_near_active = dof_near;
+ bokeh.push_constant.blur_near_begin = dof_near_begin;
+ bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; // Only used with non-physically-based.
+ bokeh.push_constant.use_physical_near = dof_near_size < 0.0;
+ bokeh.push_constant.blur_size_near = bokeh_size; // Only used with physically-based.
+
+ bokeh.push_constant.use_jitter = use_jitter;
bokeh.push_constant.jitter_seed = Math::randf() * 1000.0;
bokeh.push_constant.z_near = p_cam_znear;
bokeh.push_constant.z_far = p_cam_zfar;
bokeh.push_constant.orthogonal = p_cam_orthogonal;
- bokeh.push_constant.blur_size = p_bokeh_size;
+ bokeh.push_constant.blur_size = (dof_near_size < 0.0 && dof_far_size < 0.0) ? 32 : bokeh_size; // Cap with physically-based to keep performance reasonable.
bokeh.push_constant.second_pass = false;
bokeh.push_constant.half_size = false;
@@ -150,9 +169,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
- if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+ if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
//second pass
- BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
+ BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
@@ -160,9 +179,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
static const int quality_samples[4] = { 6, 12, 12, 24 };
- bokeh.push_constant.steps = quality_samples[p_quality];
+ bokeh.push_constant.steps = quality_samples[blur_quality];
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
//box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
@@ -187,7 +206,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
//third pass
bokeh.push_constant.second_pass = true;
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image1), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
} else {
@@ -200,7 +219,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
//forth pass, upscale for low quality
shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
@@ -232,7 +251,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
bokeh.push_constant.steps = 0;
- bokeh.push_constant.blur_scale = quality_scale[p_quality];
+ bokeh.push_constant.blur_scale = quality_scale[blur_quality];
//circle always runs in half size, otherwise too expensive
@@ -273,7 +292,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far,
RD::get_singleton()->compute_list_end();
}
-void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
+void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't blur-based depth of field with the clustered renderer.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
@@ -281,6 +300,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
+ bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes);
+ float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes);
+ float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes);
+ bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes);
+ float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes);
+ float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes);
+ float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius.
+
+ RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape();
+ RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality();
+
// setup our base push constant
memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant));
@@ -292,7 +322,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
bokeh.push_constant.second_pass = false;
bokeh.push_constant.half_size = false;
- bokeh.push_constant.blur_size = p_dof_blur_amount;
+ bokeh.push_constant.blur_size = bokeh_size;
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
@@ -307,17 +337,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
RD::Uniform u_weight_texture2(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[2] }));
RD::Uniform u_weight_texture3(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[3] }));
- if (p_dof_far || p_dof_near) {
- if (p_dof_far) {
+ if (dof_far || dof_near) {
+ if (dof_far) {
bokeh.push_constant.blur_far_active = true;
- bokeh.push_constant.blur_far_begin = p_dof_far_begin;
- bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size;
+ bokeh.push_constant.blur_far_begin = dof_far_begin;
+ bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size;
}
- if (p_dof_near) {
+ if (dof_near) {
bokeh.push_constant.blur_near_active = true;
- bokeh.push_constant.blur_near_begin = p_dof_near_begin;
- bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size;
+ bokeh.push_constant.blur_near_begin = dof_near_begin;
+ bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size;
}
{
@@ -337,14 +367,14 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
RD::get_singleton()->draw_list_end();
}
- if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
+ if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
// double pass approach
- BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
+ BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL;
RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
- if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) {
+ if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) {
//box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes)
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
@@ -354,7 +384,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
static const int quality_samples[4] = { 6, 12, 12, 24 };
bokeh.push_constant.blur_scale = 0.5;
- bokeh.push_constant.steps = quality_samples[p_quality];
+ bokeh.push_constant.steps = quality_samples[blur_quality];
RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
@@ -373,7 +403,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
// Pass 2
if (!bokeh.push_constant.half_size) {
// do not output weight, we're writing back into our base buffer
- mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
+ mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT;
shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
@@ -432,7 +462,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f
}
static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
- bokeh.push_constant.blur_scale = quality_scale[p_quality];
+ bokeh.push_constant.blur_scale = quality_scale[blur_quality];
bokeh.push_constant.steps = 0.0;
RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.h b/servers/rendering/renderer_rd/effects/bokeh_dof.h
index d7b736119c..33dbdfcdc1 100644
--- a/servers/rendering/renderer_rd/effects/bokeh_dof.h
+++ b/servers/rendering/renderer_rd/effects/bokeh_dof.h
@@ -66,6 +66,11 @@ private:
uint32_t use_jitter;
float jitter_seed;
+ uint32_t use_physical_near;
+ uint32_t use_physical_far;
+
+ float blur_size_near;
+ float blur_size_far;
uint32_t pad[2];
};
@@ -111,10 +116,10 @@ public:
BokehDOF(bool p_prefer_raster_effects);
~BokehDOF();
- void bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
- void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+ void bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
+ void bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal);
};
} // namespace RendererRD
-#endif // !BOKEH_DOF_RD_H
+#endif // BOKEH_DOF_RD_H
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index cbf7046887..53237c1dfb 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -249,6 +249,56 @@ CopyEffects::CopyEffects(bool p_prefer_raster_effects) {
roughness.raster_pipeline.clear();
}
}
+
+ {
+ Vector<String> specular_modes;
+ specular_modes.push_back("\n#define MODE_MERGE\n"); // SPECULAR_MERGE_ADD
+ specular_modes.push_back("\n#define MODE_MERGE\n#define MODE_SSR\n"); // SPECULAR_MERGE_SSR
+ specular_modes.push_back("\n"); // SPECULAR_MERGE_ADDITIVE_ADD
+ specular_modes.push_back("\n#define MODE_SSR\n"); // SPECULAR_MERGE_ADDITIVE_SSR
+
+ specular_modes.push_back("\n#define USE_MULTIVIEW\n#define MODE_MERGE\n"); // SPECULAR_MERGE_ADD_MULTIVIEW
+ specular_modes.push_back("\n#define USE_MULTIVIEW\n#define MODE_MERGE\n#define MODE_SSR\n"); // SPECULAR_MERGE_SSR_MULTIVIEW
+ specular_modes.push_back("\n#define USE_MULTIVIEW\n"); // SPECULAR_MERGE_ADDITIVE_ADD_MULTIVIEW
+ specular_modes.push_back("\n#define USE_MULTIVIEW\n#define MODE_SSR\n"); // SPECULAR_MERGE_ADDITIVE_SSR_MULTIVIEW
+
+ specular_merge.shader.initialize(specular_modes);
+
+ if (!RendererCompositorRD::singleton->is_xr_enabled()) {
+ specular_merge.shader.set_variant_enabled(SPECULAR_MERGE_ADD_MULTIVIEW, false);
+ specular_merge.shader.set_variant_enabled(SPECULAR_MERGE_SSR_MULTIVIEW, false);
+ specular_merge.shader.set_variant_enabled(SPECULAR_MERGE_ADDITIVE_ADD_MULTIVIEW, false);
+ specular_merge.shader.set_variant_enabled(SPECULAR_MERGE_ADDITIVE_SSR_MULTIVIEW, false);
+ }
+
+ specular_merge.shader_version = specular_merge.shader.version_create();
+
+ //use additive
+
+ RD::PipelineColorBlendState::Attachment ba;
+ ba.enable_blend = true;
+ ba.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ ba.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ ba.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ ba.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ ba.color_blend_op = RD::BLEND_OP_ADD;
+ ba.alpha_blend_op = RD::BLEND_OP_ADD;
+
+ RD::PipelineColorBlendState blend_additive;
+ blend_additive.attachments.push_back(ba);
+
+ for (int i = 0; i < SPECULAR_MERGE_MAX; i++) {
+ if (specular_merge.shader.is_variant_enabled(i)) {
+ RD::PipelineColorBlendState blend_state;
+ if (i == SPECULAR_MERGE_ADDITIVE_ADD || i == SPECULAR_MERGE_ADDITIVE_SSR || i == SPECULAR_MERGE_ADDITIVE_ADD_MULTIVIEW || i == SPECULAR_MERGE_ADDITIVE_SSR_MULTIVIEW) {
+ blend_state = blend_additive;
+ } else {
+ blend_state = RD::PipelineColorBlendState::create_disabled();
+ }
+ specular_merge.pipelines[i].setup(specular_merge.shader.version_get_shader(specular_merge.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
+ }
+ }
+ }
}
CopyEffects::~CopyEffects() {
@@ -264,6 +314,8 @@ CopyEffects::~CopyEffects() {
roughness.compute_shader.version_free(roughness.shader_version);
}
+ specular_merge.shader.version_free(specular_merge.shader_version);
+
RD::get_singleton()->free(filter.coefficient_buffer);
if (RD::get_singleton()->uniform_set_is_valid(filter.image_uniform_set)) {
@@ -602,7 +654,7 @@ void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Re
RD::get_singleton()->compute_list_end();
}
-void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
@@ -626,7 +678,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con
copy.push_constant.glow_white = 0; //actually unused
copy.push_constant.glow_luminance_cap = p_luminance_cap;
- copy.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
+ copy.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
@@ -653,7 +705,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con
RD::get_singleton()->compute_list_end();
}
-void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) {
+void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer.");
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
@@ -661,6 +713,9 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
+ RID half_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_half_texture);
+ RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture);
+
memset(&blur_raster.push_constant, 0, sizeof(BlurRasterPushConstant));
BlurRasterMode blur_mode = p_first_pass && p_auto_exposure.is_valid() ? BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE : BLUR_MODE_GAUSSIAN_GLOW;
@@ -677,7 +732,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc
blur_raster.push_constant.glow_white = 0; //actually unused
blur_raster.push_constant.glow_luminance_cap = p_luminance_cap;
- blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also
+ blur_raster.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also
blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier;
@@ -685,14 +740,14 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
- RD::Uniform u_rd_texture_half(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_rd_texture_half }));
+ RD::Uniform u_half_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_half_texture }));
RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode);
ERR_FAIL_COND(shader.is_null());
//HORIZONTAL
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer_half, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer_half)));
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
if (p_auto_exposure.is_valid() && p_first_pass) {
RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure }));
@@ -712,9 +767,9 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc
ERR_FAIL_COND(shader.is_null());
//VERTICAL
- draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_rd_texture_half), 0);
+ draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
blur_raster.push_constant.flags = base_flags;
@@ -758,9 +813,11 @@ void CopyEffects::make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const
RD::get_singleton()->compute_list_end();
}
-void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size) {
+void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size) {
ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of mipmap with the clustered renderer.");
+ RID dest_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_dest_texture);
+
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -781,8 +838,8 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebu
RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant));
@@ -1083,3 +1140,57 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f
RD::get_singleton()->draw_list_draw(draw_list, true);
RD::get_singleton()->draw_list_end();
}
+
+void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection, uint32_t p_view_count) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::get_singleton()->draw_command_begin_label("Merge specular");
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, Vector<Color>());
+
+ int mode;
+ if (p_reflection.is_valid()) {
+ if (p_base.is_valid()) {
+ mode = SPECULAR_MERGE_SSR;
+ } else {
+ mode = SPECULAR_MERGE_ADDITIVE_SSR;
+ }
+ } else {
+ if (p_base.is_valid()) {
+ mode = SPECULAR_MERGE_ADD;
+ } else {
+ mode = SPECULAR_MERGE_ADDITIVE_ADD;
+ }
+ }
+
+ if (p_view_count > 1) {
+ mode += SPECULAR_MERGE_ADD_MULTIVIEW;
+ }
+
+ RID shader = specular_merge.shader.version_get_shader(specular_merge.shader_version, mode);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, specular_merge.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+
+ if (p_base.is_valid()) {
+ RD::Uniform u_base(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_base }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_base), 2);
+ }
+
+ RD::Uniform u_specular(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_specular }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_specular), 0);
+
+ if (p_reflection.is_valid()) {
+ RD::Uniform u_reflection(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_reflection }));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_reflection), 1);
+ }
+
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+ RD::get_singleton()->draw_list_end();
+
+ RD::get_singleton()->draw_command_end_label();
+}
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h
index 882b446964..0ddb60ebef 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.h
+++ b/servers/rendering/renderer_rd/effects/copy_effects.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef COPY_RD_H
-#define COPY_RD_H
+#ifndef COPY_EFFECTS_RD_H
+#define COPY_EFFECTS_RD_H
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl.gen.h"
@@ -42,6 +42,7 @@
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_roughness.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering_server.h"
@@ -85,7 +86,7 @@ private:
float glow_exposure;
float glow_white;
float glow_luminance_cap;
- float glow_auto_exposure_grey;
+ float glow_auto_exposure_scale;
float luminance_multiplier;
float res1;
@@ -147,7 +148,7 @@ private:
float glow_exposure;
float glow_white;
float glow_luminance_cap;
- float glow_auto_exposure_grey;
+ float glow_auto_exposure_scale;
// DOF.
float camera_z_far;
float camera_z_near;
@@ -274,6 +275,33 @@ private:
PipelineCacheRD raster_pipeline;
} roughness;
+ // Merge specular
+
+ enum SpecularMergeMode {
+ SPECULAR_MERGE_ADD,
+ SPECULAR_MERGE_SSR,
+ SPECULAR_MERGE_ADDITIVE_ADD,
+ SPECULAR_MERGE_ADDITIVE_SSR,
+
+ SPECULAR_MERGE_ADD_MULTIVIEW,
+ SPECULAR_MERGE_SSR_MULTIVIEW,
+ SPECULAR_MERGE_ADDITIVE_ADD_MULTIVIEW,
+ SPECULAR_MERGE_ADDITIVE_SSR_MULTIVIEW,
+
+ SPECULAR_MERGE_MAX
+ };
+
+ /* Specular merge must be done using raster, rather than compute
+ * because it must continue the existing color buffer
+ */
+
+ struct SpecularMerge {
+ SpecularMergeShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipelines[SPECULAR_MERGE_MAX];
+
+ } specular_merge;
+
static CopyEffects *singleton;
public:
@@ -293,11 +321,11 @@ public:
void copy_raster(RID p_source_texture, RID p_dest_framebuffer);
void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false);
- void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
- void gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
+ void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);
+ void gaussian_glow_raster(RID p_source_rd_texture, RID p_half_texture, RID p_dest_texture, float p_luminance_multiplier, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0);
void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
- void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size);
+ void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size);
void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
@@ -309,8 +337,10 @@ public:
void cubemap_roughness(RID p_source_rd_texture, RID p_dest_texture, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
void cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_sample_count, float p_roughness, float p_size);
+
+ void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection, uint32_t p_view_count);
};
} // namespace RendererRD
-#endif // !COPY_RD_H
+#endif // COPY_EFFECTS_RD_H
diff --git a/servers/rendering/renderer_rd/effects/fsr.cpp b/servers/rendering/renderer_rd/effects/fsr.cpp
new file mode 100644
index 0000000000..92b34ede0e
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/fsr.cpp
@@ -0,0 +1,128 @@
+/*************************************************************************/
+/* fsr.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "fsr.h"
+#include "../storage_rd/material_storage.h"
+#include "../uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+FSR::FSR() {
+ Vector<String> FSR_upscale_modes;
+
+#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
+ // MoltenVK does not support some of the operations used by the normal mode of FSR. Fallback works just fine though.
+ FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n");
+#else
+ // Everyone else can use normal mode when available.
+ if (RD::get_singleton()->has_feature(RD::SUPPORTS_FSR_HALF_FLOAT)) {
+ FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_NORMAL\n");
+ } else {
+ FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n");
+ }
+#endif
+
+ fsr_shader.initialize(FSR_upscale_modes);
+
+ shader_version = fsr_shader.version_create();
+ pipeline = RD::get_singleton()->compute_pipeline_create(fsr_shader.version_get_shader(shader_version, 0));
+}
+
+FSR::~FSR() {
+ fsr_shader.version_free(shader_version);
+}
+
+void FSR::fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ Size2i internal_size = p_render_buffers->get_internal_size();
+ Size2i target_size = p_render_buffers->get_target_size();
+ float fsr_upscale_sharpness = p_render_buffers->get_fsr_sharpness();
+
+ if (!p_render_buffers->has_texture(SNAME("FSR"), SNAME("upscale_texture"))) {
+ RD::DataFormat format = p_render_buffers->get_base_data_format();
+ uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ uint32_t layers = 1; // we only need one layer, in multiview we're processing one layer at a time.
+
+ p_render_buffers->create_texture(SNAME("FSR"), SNAME("upscale_texture"), format, usage_bits, RD::TEXTURE_SAMPLES_1, target_size, layers);
+ }
+
+ RID upscale_texture = p_render_buffers->get_texture(SNAME("FSR"), SNAME("upscale_texture"));
+
+ FSRUpscalePushConstant push_constant;
+ memset(&push_constant, 0, sizeof(FSRUpscalePushConstant));
+
+ int dispatch_x = (target_size.x + 15) / 16;
+ int dispatch_y = (target_size.y + 15) / 16;
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
+
+ push_constant.resolution_width = internal_size.width;
+ push_constant.resolution_height = internal_size.height;
+ push_constant.upscaled_width = target_size.width;
+ push_constant.upscaled_height = target_size.height;
+ push_constant.sharpness = fsr_upscale_sharpness;
+
+ RID shader = fsr_shader.version_get_shader(shader_version, 0);
+ ERR_FAIL_COND(shader.is_null());
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ //FSR Easc
+ RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, p_source_rd_texture });
+ RD::Uniform u_upscale_texture(RD::UNIFORM_TYPE_IMAGE, 0, { upscale_texture });
+
+ push_constant.pass = FSR_UPSCALE_PASS_EASU;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_upscale_texture), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //FSR Rcas
+ RD::Uniform u_upscale_texture_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, upscale_texture });
+ RD::Uniform u_destination_texture(RD::UNIFORM_TYPE_IMAGE, 0, { p_destination_texture });
+
+ push_constant.pass = FSR_UPSCALE_PASS_RCAS;
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_upscale_texture_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_destination_texture), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1);
+
+ RD::get_singleton()->compute_list_end(compute_list);
+}
diff --git a/servers/rendering/renderer_rd/effects/fsr.h b/servers/rendering/renderer_rd/effects/fsr.h
new file mode 100644
index 0000000000..69088e526a
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/fsr.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* fsr.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FSR_RD_H
+#define FSR_RD_H
+
+#include "../pipeline_cache_rd.h"
+#include "../shaders/effects/fsr_upscale.glsl.gen.h"
+#include "../storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class FSR {
+public:
+ FSR();
+ ~FSR();
+
+ void fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture);
+
+private:
+ enum FSRUpscalePass {
+ FSR_UPSCALE_PASS_EASU = 0,
+ FSR_UPSCALE_PASS_RCAS = 1
+ };
+
+ struct FSRUpscalePushConstant {
+ float resolution_width;
+ float resolution_height;
+ float upscaled_width;
+ float upscaled_height;
+ float sharpness;
+ int pass;
+ int _unused0, _unused1;
+ };
+
+ FsrUpscaleShaderRD fsr_shader;
+ RID shader_version;
+ RID pipeline;
+};
+
+} // namespace RendererRD
+
+#endif // FSR_RD_H
diff --git a/servers/rendering/renderer_rd/effects/resolve.h b/servers/rendering/renderer_rd/effects/resolve.h
index d4b24a610f..2a4cd06827 100644
--- a/servers/rendering/renderer_rd/effects/resolve.h
+++ b/servers/rendering/renderer_rd/effects/resolve.h
@@ -71,4 +71,4 @@ public:
} // namespace RendererRD
-#endif // !RESOLVE_RD_H
+#endif // RESOLVE_RD_H
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp
new file mode 100644
index 0000000000..9653382e96
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp
@@ -0,0 +1,1876 @@
+/*************************************************************************/
+/* ss_effects.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "ss_effects.h"
+
+#include "core/config/project_settings.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+SSEffects *SSEffects::singleton = nullptr;
+
+static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ p_array[i * 4 + j] = p_mtx.columns[i][j];
+ }
+ }
+}
+
+SSEffects::SSEffects() {
+ singleton = this;
+
+ // Initialize depth buffer for screen space effects
+ {
+ Vector<String> downsampler_modes;
+ downsampler_modes.push_back("\n");
+ downsampler_modes.push_back("\n#define USE_HALF_SIZE\n");
+ downsampler_modes.push_back("\n#define GENERATE_MIPS\n");
+ downsampler_modes.push_back("\n#define GENERATE_MIPS\n#define USE_HALF_SIZE\n");
+ downsampler_modes.push_back("\n#define USE_HALF_BUFFERS\n");
+ downsampler_modes.push_back("\n#define USE_HALF_BUFFERS\n#define USE_HALF_SIZE\n");
+ downsampler_modes.push_back("\n#define GENERATE_MIPS\n#define GENERATE_FULL_MIPS");
+
+ ss_effects.downsample_shader.initialize(downsampler_modes);
+
+ ss_effects.downsample_shader_version = ss_effects.downsample_shader.version_create();
+
+ for (int i = 0; i < SS_EFFECTS_MAX; i++) {
+ ss_effects.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ss_effects.downsample_shader.version_get_shader(ss_effects.downsample_shader_version, i));
+ }
+
+ ss_effects.gather_constants_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SSEffectsGatherConstants));
+ SSEffectsGatherConstants gather_constants;
+
+ const int sub_pass_count = 5;
+ for (int pass = 0; pass < 4; pass++) {
+ for (int subPass = 0; subPass < sub_pass_count; subPass++) {
+ int a = pass;
+ int b = subPass;
+
+ int spmap[5]{ 0, 1, 4, 3, 2 };
+ b = spmap[subPass];
+
+ float ca, sa;
+ float angle0 = (float(a) + float(b) / float(sub_pass_count)) * Math_PI * 0.5f;
+
+ ca = Math::cos(angle0);
+ sa = Math::sin(angle0);
+
+ float scale = 1.0f + (a - 1.5f + (b - (sub_pass_count - 1.0f) * 0.5f) / float(sub_pass_count)) * 0.07f;
+
+ gather_constants.rotation_matrices[pass * 20 + subPass * 4 + 0] = scale * ca;
+ gather_constants.rotation_matrices[pass * 20 + subPass * 4 + 1] = scale * -sa;
+ gather_constants.rotation_matrices[pass * 20 + subPass * 4 + 2] = -scale * sa;
+ gather_constants.rotation_matrices[pass * 20 + subPass * 4 + 3] = -scale * ca;
+ }
+ }
+
+ RD::get_singleton()->buffer_update(ss_effects.gather_constants_buffer, 0, sizeof(SSEffectsGatherConstants), &gather_constants);
+ }
+
+ // Initialize Screen Space Indirect Lighting (SSIL)
+ ssil_set_quality(RS::EnvironmentSSILQuality(int(GLOBAL_GET("rendering/environment/ssil/quality"))), GLOBAL_GET("rendering/environment/ssil/half_size"), GLOBAL_GET("rendering/environment/ssil/adaptive_target"), GLOBAL_GET("rendering/environment/ssil/blur_passes"), GLOBAL_GET("rendering/environment/ssil/fadeout_from"), GLOBAL_GET("rendering/environment/ssil/fadeout_to"));
+
+ {
+ Vector<String> ssil_modes;
+ ssil_modes.push_back("\n");
+ ssil_modes.push_back("\n#define SSIL_BASE\n");
+ ssil_modes.push_back("\n#define ADAPTIVE\n");
+
+ ssil.gather_shader.initialize(ssil_modes);
+
+ ssil.gather_shader_version = ssil.gather_shader.version_create();
+
+ for (int i = SSIL_GATHER; i <= SSIL_GATHER_ADAPTIVE; i++) {
+ ssil.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ssil.gather_shader.version_get_shader(ssil.gather_shader_version, i));
+ }
+ ssil.projection_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SSILProjectionUniforms));
+ }
+
+ {
+ Vector<String> ssil_modes;
+ ssil_modes.push_back("\n#define GENERATE_MAP\n");
+ ssil_modes.push_back("\n#define PROCESS_MAPA\n");
+ ssil_modes.push_back("\n#define PROCESS_MAPB\n");
+
+ ssil.importance_map_shader.initialize(ssil_modes);
+
+ ssil.importance_map_shader_version = ssil.importance_map_shader.version_create();
+
+ for (int i = SSIL_GENERATE_IMPORTANCE_MAP; i <= SSIL_PROCESS_IMPORTANCE_MAPB; i++) {
+ ssil.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ssil.importance_map_shader.version_get_shader(ssil.importance_map_shader_version, i - SSIL_GENERATE_IMPORTANCE_MAP));
+ }
+ ssil.importance_map_load_counter = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t));
+ int zero[1] = { 0 };
+ RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero);
+ RD::get_singleton()->set_resource_name(ssil.importance_map_load_counter, "Importance Map Load Counter");
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 0;
+ u.append_id(ssil.importance_map_load_counter);
+ uniforms.push_back(u);
+ }
+ ssil.counter_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssil.importance_map_shader.version_get_shader(ssil.importance_map_shader_version, 2), 2);
+ RD::get_singleton()->set_resource_name(ssil.counter_uniform_set, "Load Counter Uniform Set");
+ }
+
+ {
+ Vector<String> ssil_modes;
+ ssil_modes.push_back("\n#define MODE_NON_SMART\n");
+ ssil_modes.push_back("\n#define MODE_SMART\n");
+ ssil_modes.push_back("\n#define MODE_WIDE\n");
+
+ ssil.blur_shader.initialize(ssil_modes);
+
+ ssil.blur_shader_version = ssil.blur_shader.version_create();
+ for (int i = SSIL_BLUR_PASS; i <= SSIL_BLUR_PASS_WIDE; i++) {
+ ssil.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ssil.blur_shader.version_get_shader(ssil.blur_shader_version, i - SSIL_BLUR_PASS));
+ }
+ }
+
+ {
+ Vector<String> ssil_modes;
+ ssil_modes.push_back("\n#define MODE_NON_SMART\n");
+ ssil_modes.push_back("\n#define MODE_SMART\n");
+ ssil_modes.push_back("\n#define MODE_HALF\n");
+
+ ssil.interleave_shader.initialize(ssil_modes);
+
+ ssil.interleave_shader_version = ssil.interleave_shader.version_create();
+ for (int i = SSIL_INTERLEAVE; i <= SSIL_INTERLEAVE_HALF; i++) {
+ ssil.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ssil.interleave_shader.version_get_shader(ssil.interleave_shader_version, i - SSIL_INTERLEAVE));
+ }
+ }
+
+ // Initialize Screen Space Ambient Occlusion (SSAO)
+ ssao_set_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to"));
+
+ {
+ RD::SamplerState sampler;
+ sampler.mag_filter = RD::SAMPLER_FILTER_NEAREST;
+ sampler.min_filter = RD::SAMPLER_FILTER_NEAREST;
+ sampler.mip_filter = RD::SAMPLER_FILTER_NEAREST;
+ sampler.repeat_u = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT;
+ sampler.repeat_v = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT;
+ sampler.repeat_w = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT;
+ sampler.max_lod = 4;
+
+ uint32_t pipeline = 0;
+ {
+ Vector<String> ssao_modes;
+
+ ssao_modes.push_back("\n");
+ ssao_modes.push_back("\n#define SSAO_BASE\n");
+ ssao_modes.push_back("\n#define ADAPTIVE\n");
+
+ ssao.gather_shader.initialize(ssao_modes);
+
+ ssao.gather_shader_version = ssao.gather_shader.version_create();
+
+ for (int i = 0; i <= SSAO_GATHER_ADAPTIVE; i++) {
+ ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.gather_shader.version_get_shader(ssao.gather_shader_version, i));
+ pipeline++;
+ }
+ }
+
+ {
+ Vector<String> ssao_modes;
+ ssao_modes.push_back("\n#define GENERATE_MAP\n");
+ ssao_modes.push_back("\n#define PROCESS_MAPA\n");
+ ssao_modes.push_back("\n#define PROCESS_MAPB\n");
+
+ ssao.importance_map_shader.initialize(ssao_modes);
+
+ ssao.importance_map_shader_version = ssao.importance_map_shader.version_create();
+
+ for (int i = SSAO_GENERATE_IMPORTANCE_MAP; i <= SSAO_PROCESS_IMPORTANCE_MAPB; i++) {
+ ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.importance_map_shader.version_get_shader(ssao.importance_map_shader_version, i - SSAO_GENERATE_IMPORTANCE_MAP));
+
+ pipeline++;
+ }
+
+ ssao.importance_map_load_counter = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t));
+ int zero[1] = { 0 };
+ RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero);
+ RD::get_singleton()->set_resource_name(ssao.importance_map_load_counter, "Importance Map Load Counter");
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 0;
+ u.append_id(ssao.importance_map_load_counter);
+ uniforms.push_back(u);
+ }
+ ssao.counter_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.importance_map_shader.version_get_shader(ssao.importance_map_shader_version, 2), 2);
+ RD::get_singleton()->set_resource_name(ssao.counter_uniform_set, "Load Counter Uniform Set");
+ }
+
+ {
+ Vector<String> ssao_modes;
+ ssao_modes.push_back("\n#define MODE_NON_SMART\n");
+ ssao_modes.push_back("\n#define MODE_SMART\n");
+ ssao_modes.push_back("\n#define MODE_WIDE\n");
+
+ ssao.blur_shader.initialize(ssao_modes);
+
+ ssao.blur_shader_version = ssao.blur_shader.version_create();
+
+ for (int i = SSAO_BLUR_PASS; i <= SSAO_BLUR_PASS_WIDE; i++) {
+ ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.blur_shader.version_get_shader(ssao.blur_shader_version, i - SSAO_BLUR_PASS));
+
+ pipeline++;
+ }
+ }
+
+ {
+ Vector<String> ssao_modes;
+ ssao_modes.push_back("\n#define MODE_NON_SMART\n");
+ ssao_modes.push_back("\n#define MODE_SMART\n");
+ ssao_modes.push_back("\n#define MODE_HALF\n");
+
+ ssao.interleave_shader.initialize(ssao_modes);
+
+ ssao.interleave_shader_version = ssao.interleave_shader.version_create();
+ for (int i = SSAO_INTERLEAVE; i <= SSAO_INTERLEAVE_HALF; i++) {
+ ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.interleave_shader.version_get_shader(ssao.interleave_shader_version, i - SSAO_INTERLEAVE));
+ RD::get_singleton()->set_resource_name(ssao.pipelines[pipeline], "Interleave Pipeline " + itos(i));
+ pipeline++;
+ }
+ }
+
+ ERR_FAIL_COND(pipeline != SSAO_MAX);
+
+ ss_effects.mirror_sampler = RD::get_singleton()->sampler_create(sampler);
+ }
+
+ // Screen Space Reflections
+ ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/environment/screen_space_reflection/roughness_quality")));
+
+ {
+ Vector<RD::PipelineSpecializationConstant> specialization_constants;
+
+ {
+ RD::PipelineSpecializationConstant sc;
+ sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
+ sc.constant_id = 0; // SSR_USE_FULL_PROJECTION_MATRIX
+ sc.bool_value = false;
+ specialization_constants.push_back(sc);
+ }
+
+ {
+ Vector<String> ssr_scale_modes;
+ ssr_scale_modes.push_back("\n");
+
+ ssr_scale.shader.initialize(ssr_scale_modes);
+ ssr_scale.shader_version = ssr_scale.shader.version_create();
+
+ for (int v = 0; v < SSR_VARIATIONS; v++) {
+ specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false;
+ ssr_scale.pipelines[v] = RD::get_singleton()->compute_pipeline_create(ssr_scale.shader.version_get_shader(ssr_scale.shader_version, 0), specialization_constants);
+ }
+ }
+
+ {
+ Vector<String> ssr_modes;
+ ssr_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_NORMAL
+ ssr_modes.push_back("\n#define MODE_ROUGH\n"); // SCREEN_SPACE_REFLECTION_ROUGH
+
+ ssr.shader.initialize(ssr_modes);
+ ssr.shader_version = ssr.shader.version_create();
+
+ for (int v = 0; v < SSR_VARIATIONS; v++) {
+ specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false;
+ for (int i = 0; i < SCREEN_SPACE_REFLECTION_MAX; i++) {
+ ssr.pipelines[v][i] = RD::get_singleton()->compute_pipeline_create(ssr.shader.version_get_shader(ssr.shader_version, i), specialization_constants);
+ }
+ }
+ }
+
+ {
+ Vector<String> ssr_filter_modes;
+ ssr_filter_modes.push_back("\n"); // SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL
+ ssr_filter_modes.push_back("\n#define VERTICAL_PASS\n"); // SCREEN_SPACE_REFLECTION_FILTER_VERTICAL
+
+ ssr_filter.shader.initialize(ssr_filter_modes);
+ ssr_filter.shader_version = ssr_filter.shader.version_create();
+
+ for (int v = 0; v < SSR_VARIATIONS; v++) {
+ specialization_constants.ptrw()[0].bool_value = (v & SSR_MULTIVIEW) ? true : false;
+ for (int i = 0; i < SCREEN_SPACE_REFLECTION_FILTER_MAX; i++) {
+ ssr_filter.pipelines[v][i] = RD::get_singleton()->compute_pipeline_create(ssr_filter.shader.version_get_shader(ssr_filter.shader_version, i), specialization_constants);
+ }
+ }
+ }
+ }
+
+ // Subsurface scattering
+ sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality")));
+ sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale");
+ sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale");
+
+ {
+ Vector<String> sss_modes;
+ sss_modes.push_back("\n#define USE_11_SAMPLES\n");
+ sss_modes.push_back("\n#define USE_17_SAMPLES\n");
+ sss_modes.push_back("\n#define USE_25_SAMPLES\n");
+
+ sss.shader.initialize(sss_modes);
+
+ sss.shader_version = sss.shader.version_create();
+
+ for (int i = 0; i < sss_modes.size(); i++) {
+ sss.pipelines[i] = RD::get_singleton()->compute_pipeline_create(sss.shader.version_get_shader(sss.shader_version, i));
+ }
+ }
+}
+
+SSEffects::~SSEffects() {
+ {
+ // Cleanup SS Reflections
+ ssr.shader.version_free(ssr.shader_version);
+ ssr_filter.shader.version_free(ssr_filter.shader_version);
+ ssr_scale.shader.version_free(ssr_scale.shader_version);
+
+ if (ssr.ubo.is_valid()) {
+ RD::get_singleton()->free(ssr.ubo);
+ }
+ }
+
+ {
+ // Cleanup SS downsampler
+ ss_effects.downsample_shader.version_free(ss_effects.downsample_shader_version);
+
+ RD::get_singleton()->free(ss_effects.mirror_sampler);
+ RD::get_singleton()->free(ss_effects.gather_constants_buffer);
+ }
+
+ {
+ // Cleanup SSIL
+ ssil.blur_shader.version_free(ssil.blur_shader_version);
+ ssil.gather_shader.version_free(ssil.gather_shader_version);
+ ssil.interleave_shader.version_free(ssil.interleave_shader_version);
+ ssil.importance_map_shader.version_free(ssil.importance_map_shader_version);
+
+ RD::get_singleton()->free(ssil.importance_map_load_counter);
+ RD::get_singleton()->free(ssil.projection_uniform_buffer);
+ }
+
+ {
+ // Cleanup SSAO
+ ssao.blur_shader.version_free(ssao.blur_shader_version);
+ ssao.gather_shader.version_free(ssao.gather_shader_version);
+ ssao.interleave_shader.version_free(ssao.interleave_shader_version);
+ ssao.importance_map_shader.version_free(ssao.importance_map_shader_version);
+
+ RD::get_singleton()->free(ssao.importance_map_load_counter);
+ }
+
+ {
+ // Cleanup Subsurface scattering
+ sss.shader.version_free(sss.shader_version);
+ }
+
+ singleton = nullptr;
+}
+
+/* SS Downsampler */
+
+void SSEffects::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, bool p_invalidate_uniform_set, Size2i p_full_screen_size, const Projection &p_projection) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ // Downsample and deinterleave the depth buffer for SSAO and SSIL
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ int downsample_mode = SS_EFFECTS_DOWNSAMPLE;
+ bool use_mips = ssao_quality > RS::ENV_SSAO_QUALITY_MEDIUM || ssil_quality > RS::ENV_SSIL_QUALITY_MEDIUM;
+
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW && ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ downsample_mode = SS_EFFECTS_DOWNSAMPLE_HALF;
+ } else if (use_mips) {
+ downsample_mode = SS_EFFECTS_DOWNSAMPLE_MIPMAP;
+ }
+
+ bool use_half_size = false;
+ bool use_full_mips = false;
+
+ if (ssao_half_size && ssil_half_size) {
+ downsample_mode++;
+ use_half_size = true;
+ } else if (ssao_half_size != ssil_half_size) {
+ if (use_mips) {
+ downsample_mode = SS_EFFECTS_DOWNSAMPLE_FULL_MIPS;
+ use_full_mips = true;
+ } else {
+ // Only need the first two mipmaps, but the cost to generate the next two is trivial
+ // TODO investigate the benefit of a shader version to generate only 2 mips
+ downsample_mode = SS_EFFECTS_DOWNSAMPLE_MIPMAP;
+ use_mips = true;
+ }
+ }
+
+ int depth_index = use_half_size ? 1 : 0;
+
+ RD::get_singleton()->draw_command_begin_label("Downsample Depth");
+ if (p_invalidate_uniform_set || use_full_mips != ss_effects.used_full_mips_last_frame || use_half_size != ss_effects.used_half_size_last_frame || use_mips != ss_effects.used_mips_last_frame) {
+ if (ss_effects.downsample_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(ss_effects.downsample_uniform_set)) {
+ RD::get_singleton()->free(ss_effects.downsample_uniform_set);
+ ss_effects.downsample_uniform_set = RID();
+ }
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 0;
+ u.append_id(p_depth_mipmaps[depth_index + 1]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.append_id(p_depth_mipmaps[depth_index + 2]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 2;
+ u.append_id(p_depth_mipmaps[depth_index + 3]);
+ uniforms.push_back(u);
+ }
+ if (use_full_mips) {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ u.append_id(p_depth_mipmaps[4]);
+ uniforms.push_back(u);
+ }
+ ss_effects.downsample_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ss_effects.downsample_shader.version_get_shader(ss_effects.downsample_shader_version, use_full_mips ? 6 : 2), 2);
+ }
+
+ float depth_linearize_mul = -p_projection.columns[3][2];
+ float depth_linearize_add = p_projection.columns[2][2];
+ if (depth_linearize_mul * depth_linearize_add < 0) {
+ depth_linearize_add = -depth_linearize_add;
+ }
+
+ ss_effects.downsample_push_constant.orthogonal = p_projection.is_orthogonal();
+ ss_effects.downsample_push_constant.z_near = depth_linearize_mul;
+ ss_effects.downsample_push_constant.z_far = depth_linearize_add;
+ if (ss_effects.downsample_push_constant.orthogonal) {
+ ss_effects.downsample_push_constant.z_near = p_projection.get_z_near();
+ ss_effects.downsample_push_constant.z_far = p_projection.get_z_far();
+ }
+ ss_effects.downsample_push_constant.pixel_size[0] = 1.0 / p_full_screen_size.x;
+ ss_effects.downsample_push_constant.pixel_size[1] = 1.0 / p_full_screen_size.y;
+ ss_effects.downsample_push_constant.radius_sq = 1.0;
+
+ RID shader = ss_effects.downsample_shader.version_get_shader(ss_effects.downsample_shader_version, downsample_mode);
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_depth_buffer(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_depth_buffer }));
+ RD::Uniform u_depth_mipmaps(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_depth_mipmaps[depth_index + 0] }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ss_effects.pipelines[downsample_mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_depth_buffer), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth_mipmaps), 1);
+ if (use_mips) {
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, ss_effects.downsample_uniform_set, 2);
+ }
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ss_effects.downsample_push_constant, sizeof(SSEffectsDownsamplePushConstant));
+
+ Size2i size(MAX(1, p_full_screen_size.x >> (use_half_size ? 2 : 1)), MAX(1, p_full_screen_size.y >> (use_half_size ? 2 : 1)));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, size.x, size.y, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ RD::get_singleton()->draw_command_end_label();
+
+ RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
+
+ ss_effects.used_full_mips_last_frame = use_full_mips;
+ ss_effects.used_half_size_last_frame = use_half_size;
+ ss_effects.used_mips_last_frame = use_mips;
+}
+
+/* SSIL */
+
+void SSEffects::ssil_set_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
+ ssil_quality = p_quality;
+ ssil_half_size = p_half_size;
+ ssil_adaptive_target = p_adaptive_target;
+ ssil_blur_passes = p_blur_passes;
+ ssil_fadeout_from = p_fadeout_from;
+ ssil_fadeout_to = p_fadeout_to;
+}
+
+void SSEffects::gather_ssil(RD::ComputeListID p_compute_list, const Vector<RID> p_ssil_slices, const Vector<RID> p_edges_slices, const SSILSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set, RID p_projection_uniform_set) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_gather_uniform_set, 0);
+ if ((ssil_quality == RS::ENV_SSIL_QUALITY_ULTRA) && !p_adaptive_base_pass) {
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_importance_map_uniform_set, 1);
+ }
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_projection_uniform_set, 3);
+
+ RID shader = ssil.gather_shader.version_get_shader(ssil.gather_shader_version, 0);
+
+ for (int i = 0; i < 4; i++) {
+ if ((ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) && ((i == 1) || (i == 2))) {
+ continue;
+ }
+
+ RD::Uniform u_ssil_slice(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_slices[i] }));
+ RD::Uniform u_edges_slice(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_edges_slices[i] }));
+
+ ssil.gather_push_constant.pass_coord_offset[0] = i % 2;
+ ssil.gather_push_constant.pass_coord_offset[1] = i / 2;
+ ssil.gather_push_constant.pass_uv_offset[0] = ((i % 2) - 0.0) / p_settings.full_screen_size.x;
+ ssil.gather_push_constant.pass_uv_offset[1] = ((i / 2) - 0.0) / p_settings.full_screen_size.y;
+ ssil.gather_push_constant.pass = i;
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, uniform_set_cache->get_cache(shader, 2, u_ssil_slice, u_edges_slice), 2);
+ RD::get_singleton()->compute_list_set_push_constant(p_compute_list, &ssil.gather_push_constant, sizeof(SSILGatherPushConstant));
+
+ Size2i size = Size2i(p_settings.full_screen_size.x >> (ssil_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssil_half_size ? 2 : 1));
+
+ RD::get_singleton()->compute_list_dispatch_threads(p_compute_list, size.x, size.y, 1);
+ }
+ RD::get_singleton()->compute_list_add_barrier(p_compute_list);
+}
+
+void SSEffects::ssil_allocate_buffers(SSILRenderBuffers &p_ssil_buffers, const SSILSettings &p_settings, RID p_linear_depth) {
+ if (p_ssil_buffers.half_size != ssil_half_size) {
+ ssil_free(p_ssil_buffers);
+ }
+
+ if (ssil_half_size) {
+ p_ssil_buffers.buffer_width = (p_settings.full_screen_size.x + 3) / 4;
+ p_ssil_buffers.buffer_height = (p_settings.full_screen_size.y + 3) / 4;
+ p_ssil_buffers.half_buffer_width = (p_settings.full_screen_size.x + 7) / 8;
+ p_ssil_buffers.half_buffer_height = (p_settings.full_screen_size.y + 7) / 8;
+ } else {
+ p_ssil_buffers.buffer_width = (p_settings.full_screen_size.x + 1) / 2;
+ p_ssil_buffers.buffer_height = (p_settings.full_screen_size.y + 1) / 2;
+ p_ssil_buffers.half_buffer_width = (p_settings.full_screen_size.x + 3) / 4;
+ p_ssil_buffers.half_buffer_height = (p_settings.full_screen_size.y + 3) / 4;
+ }
+
+ if (p_ssil_buffers.ssil_final.is_null()) {
+ {
+ p_ssil_buffers.depth_texture_view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_linear_depth, 0, ssil_half_size ? 1 : 0, 4, RD::TEXTURE_SLICE_2D_ARRAY);
+ }
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.width = p_settings.full_screen_size.x;
+ tf.height = p_settings.full_screen_size.y;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+ p_ssil_buffers.ssil_final = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.ssil_final, "SSIL texture");
+ RD::get_singleton()->texture_clear(p_ssil_buffers.ssil_final, Color(0, 0, 0, 0), 0, 1, 0, 1);
+ if (p_ssil_buffers.last_frame.is_null()) {
+ tf.mipmaps = 6;
+ p_ssil_buffers.last_frame = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.last_frame, "Last Frame Radiance");
+ RD::get_singleton()->texture_clear(p_ssil_buffers.last_frame, Color(0, 0, 0, 0), 0, tf.mipmaps, 0, 1);
+ for (uint32_t i = 0; i < 6; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssil_buffers.last_frame, 0, i);
+ p_ssil_buffers.last_frame_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "Last Frame Radiance Mip " + itos(i) + " ");
+ }
+ }
+ }
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.width = p_ssil_buffers.buffer_width;
+ tf.height = p_ssil_buffers.buffer_height;
+ tf.array_layers = 4;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssil_buffers.deinterleaved = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.deinterleaved, "SSIL deinterleaved buffer");
+ for (uint32_t i = 0; i < 4; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssil_buffers.deinterleaved, i, 0);
+ p_ssil_buffers.deinterleaved_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "SSIL deinterleaved buffer array " + itos(i) + " ");
+ }
+ }
+
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.width = p_ssil_buffers.buffer_width;
+ tf.height = p_ssil_buffers.buffer_height;
+ tf.array_layers = 4;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssil_buffers.pong = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.pong, "SSIL deinterleaved pong buffer");
+ for (uint32_t i = 0; i < 4; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssil_buffers.pong, i, 0);
+ p_ssil_buffers.pong_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "SSIL deinterleaved buffer pong array " + itos(i) + " ");
+ }
+ }
+
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.width = p_ssil_buffers.buffer_width;
+ tf.height = p_ssil_buffers.buffer_height;
+ tf.array_layers = 4;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssil_buffers.edges = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.edges, "SSIL edges buffer");
+ for (uint32_t i = 0; i < 4; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssil_buffers.edges, i, 0);
+ p_ssil_buffers.edges_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "SSIL edges buffer slice " + itos(i) + " ");
+ }
+ }
+
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = p_ssil_buffers.half_buffer_width;
+ tf.height = p_ssil_buffers.half_buffer_height;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssil_buffers.importance_map[0] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.importance_map[0], "SSIL Importance Map");
+ p_ssil_buffers.importance_map[1] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssil_buffers.importance_map[1], "SSIL Importance Map Pong");
+ }
+ p_ssil_buffers.half_size = ssil_half_size;
+ }
+}
+
+void SSEffects::screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const Projection &p_projection, const Projection &p_last_projection, const SSILSettings &p_settings) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RD::get_singleton()->draw_command_begin_label("Process Screen Space Indirect Lighting");
+ //Store projection info before starting the compute list
+ SSILProjectionUniforms projection_uniforms;
+ store_camera(p_last_projection, projection_uniforms.inv_last_frame_projection_matrix);
+
+ RD::get_singleton()->buffer_update(ssil.projection_uniform_buffer, 0, sizeof(SSILProjectionUniforms), &projection_uniforms);
+
+ memset(&ssil.gather_push_constant, 0, sizeof(SSILGatherPushConstant));
+
+ RID shader = ssil.gather_shader.version_get_shader(ssil.gather_shader_version, 0);
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+ RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ {
+ RD::get_singleton()->draw_command_begin_label("Gather Samples");
+ ssil.gather_push_constant.screen_size[0] = p_settings.full_screen_size.x;
+ ssil.gather_push_constant.screen_size[1] = p_settings.full_screen_size.y;
+
+ ssil.gather_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssil_buffers.buffer_width;
+ ssil.gather_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssil_buffers.buffer_height;
+ float tan_half_fov_x = 1.0 / p_projection.columns[0][0];
+ float tan_half_fov_y = 1.0 / p_projection.columns[1][1];
+ ssil.gather_push_constant.NDC_to_view_mul[0] = tan_half_fov_x * 2.0;
+ ssil.gather_push_constant.NDC_to_view_mul[1] = tan_half_fov_y * -2.0;
+ ssil.gather_push_constant.NDC_to_view_add[0] = tan_half_fov_x * -1.0;
+ ssil.gather_push_constant.NDC_to_view_add[1] = tan_half_fov_y;
+ ssil.gather_push_constant.z_near = p_projection.get_z_near();
+ ssil.gather_push_constant.z_far = p_projection.get_z_far();
+ ssil.gather_push_constant.is_orthogonal = p_projection.is_orthogonal();
+
+ ssil.gather_push_constant.half_screen_pixel_size_x025[0] = ssil.gather_push_constant.half_screen_pixel_size[0] * 0.25;
+ ssil.gather_push_constant.half_screen_pixel_size_x025[1] = ssil.gather_push_constant.half_screen_pixel_size[1] * 0.25;
+
+ ssil.gather_push_constant.radius = p_settings.radius;
+ float radius_near_limit = (p_settings.radius * 1.2f);
+ if (ssil_quality <= RS::ENV_SSIL_QUALITY_LOW) {
+ radius_near_limit *= 1.50f;
+
+ if (ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ ssil.gather_push_constant.radius *= 0.8f;
+ }
+ }
+ radius_near_limit /= tan_half_fov_y;
+ ssil.gather_push_constant.intensity = p_settings.intensity * Math_PI;
+ ssil.gather_push_constant.fade_out_mul = -1.0 / (ssil_fadeout_to - ssil_fadeout_from);
+ ssil.gather_push_constant.fade_out_add = ssil_fadeout_from / (ssil_fadeout_to - ssil_fadeout_from) + 1.0;
+ ssil.gather_push_constant.inv_radius_near_limit = 1.0f / radius_near_limit;
+ ssil.gather_push_constant.neg_inv_radius = -1.0 / ssil.gather_push_constant.radius;
+ ssil.gather_push_constant.normal_rejection_amount = p_settings.normal_rejection;
+
+ ssil.gather_push_constant.load_counter_avg_div = 9.0 / float((p_ssil_buffers.half_buffer_width) * (p_ssil_buffers.half_buffer_height) * 255);
+ ssil.gather_push_constant.adaptive_sample_limit = ssil_adaptive_target;
+
+ ssil.gather_push_constant.quality = MAX(0, ssil_quality - 1);
+ ssil.gather_push_constant.size_multiplier = ssil_half_size ? 2 : 1;
+
+ if (p_ssil_buffers.projection_uniform_set.is_null()) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 0;
+ u.append_id(default_mipmap_sampler);
+ u.append_id(p_ssil_buffers.last_frame);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 1;
+ u.append_id(ssil.projection_uniform_buffer);
+ uniforms.push_back(u);
+ }
+ p_ssil_buffers.projection_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssil.gather_shader.version_get_shader(ssil.gather_shader_version, 0), 3);
+ }
+
+ if (p_ssil_buffers.gather_uniform_set.is_null()) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 0;
+ u.append_id(default_sampler);
+ u.append_id(p_ssil_buffers.depth_texture_view);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.append_id(p_normal_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 2;
+ u.append_id(ss_effects.gather_constants_buffer);
+ uniforms.push_back(u);
+ }
+ p_ssil_buffers.gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssil.gather_shader.version_get_shader(ssil.gather_shader_version, 0), 0);
+ }
+
+ if (p_ssil_buffers.importance_map_uniform_set.is_null()) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 0;
+ u.append_id(p_ssil_buffers.pong);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 1;
+ u.append_id(default_sampler);
+ u.append_id(p_ssil_buffers.importance_map[0]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 2;
+ u.append_id(ssil.importance_map_load_counter);
+ uniforms.push_back(u);
+ }
+ p_ssil_buffers.importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssil.gather_shader.version_get_shader(ssil.gather_shader_version, 2), 1);
+ }
+
+ if (ssil_quality == RS::ENV_SSIL_QUALITY_ULTRA) {
+ RD::get_singleton()->draw_command_begin_label("Generate Importance Map");
+ ssil.importance_map_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssil_buffers.buffer_width;
+ ssil.importance_map_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssil_buffers.buffer_height;
+ ssil.importance_map_push_constant.intensity = p_settings.intensity * Math_PI;
+ //base pass
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_GATHER_BASE]);
+ gather_ssil(compute_list, p_ssil_buffers.pong_slices, p_ssil_buffers.edges_slices, p_settings, true, p_ssil_buffers.gather_uniform_set, p_ssil_buffers.importance_map_uniform_set, p_ssil_buffers.projection_uniform_set);
+
+ //generate importance map
+ RD::Uniform u_ssil_pong_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.pong }));
+ RD::Uniform u_importance_map(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.importance_map[0] }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_GENERATE_IMPORTANCE_MAP]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ssil_pong_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map), 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.importance_map_push_constant, sizeof(SSILImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssil_buffers.half_buffer_width, p_ssil_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ // process Importance Map A
+ RD::Uniform u_importance_map_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.importance_map[0] }));
+ RD::Uniform u_importance_map_pong(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.importance_map[1] }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_PROCESS_IMPORTANCE_MAPA]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_importance_map_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map_pong), 1);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.importance_map_push_constant, sizeof(SSILImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssil_buffers.half_buffer_width, p_ssil_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ // process Importance Map B
+ RD::Uniform u_importance_map_pong_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.importance_map[1] }));
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_PROCESS_IMPORTANCE_MAPB]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_importance_map_pong_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, ssil.counter_uniform_set, 2);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.importance_map_push_constant, sizeof(SSILImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssil_buffers.half_buffer_width, p_ssil_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ RD::get_singleton()->draw_command_end_label(); // Importance Map
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_GATHER_ADAPTIVE]);
+ } else {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[SSIL_GATHER]);
+ }
+
+ gather_ssil(compute_list, p_ssil_buffers.deinterleaved_slices, p_ssil_buffers.edges_slices, p_settings, false, p_ssil_buffers.gather_uniform_set, p_ssil_buffers.importance_map_uniform_set, p_ssil_buffers.projection_uniform_set);
+ RD::get_singleton()->draw_command_end_label(); //Gather
+ }
+
+ {
+ RD::get_singleton()->draw_command_begin_label("Edge Aware Blur");
+ ssil.blur_push_constant.edge_sharpness = 1.0 - p_settings.sharpness;
+ ssil.blur_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssil_buffers.buffer_width;
+ ssil.blur_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssil_buffers.buffer_height;
+
+ int blur_passes = ssil_quality > RS::ENV_SSIL_QUALITY_VERY_LOW ? ssil_blur_passes : 1;
+
+ shader = ssil.blur_shader.version_get_shader(ssil.blur_shader_version, 0);
+
+ for (int pass = 0; pass < blur_passes; pass++) {
+ int blur_pipeline = SSIL_BLUR_PASS;
+ if (ssil_quality > RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ blur_pipeline = SSIL_BLUR_PASS_SMART;
+ if (pass < blur_passes - 2) {
+ blur_pipeline = SSIL_BLUR_PASS_WIDE;
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if ((ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) && ((i == 1) || (i == 2))) {
+ continue;
+ }
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[blur_pipeline]);
+ if (pass % 2 == 0) {
+ if (ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ RD::Uniform u_ssil_slice(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ssil_slice), 0);
+ } else {
+ RD::Uniform u_ssil_slice(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ ss_effects.mirror_sampler, p_ssil_buffers.deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ssil_slice), 0);
+ }
+
+ RD::Uniform u_ssil_pong_slice(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ssil_pong_slice), 1);
+ } else {
+ if (ssil_quality == RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ RD::Uniform u_ssil_pong_slice(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ssil_pong_slice), 0);
+ } else {
+ RD::Uniform u_ssil_pong_slice(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ ss_effects.mirror_sampler, p_ssil_buffers.pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ssil_pong_slice), 0);
+ }
+
+ RD::Uniform u_ssil_slice(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ssil_slice), 1);
+ }
+
+ RD::Uniform u_edges_slice(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.edges_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_edges_slice), 2);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.blur_push_constant, sizeof(SSILBlurPushConstant));
+
+ int x_groups = (p_settings.full_screen_size.x >> (ssil_half_size ? 2 : 1));
+ int y_groups = (p_settings.full_screen_size.y >> (ssil_half_size ? 2 : 1));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, x_groups, y_groups, 1);
+ if (ssil_quality > RS::ENV_SSIL_QUALITY_VERY_LOW) {
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ }
+ }
+ }
+
+ RD::get_singleton()->draw_command_end_label(); // Blur
+ }
+
+ {
+ RD::get_singleton()->draw_command_begin_label("Interleave Buffers");
+ ssil.interleave_push_constant.inv_sharpness = 1.0 - p_settings.sharpness;
+ ssil.interleave_push_constant.pixel_size[0] = 1.0 / p_settings.full_screen_size.x;
+ ssil.interleave_push_constant.pixel_size[1] = 1.0 / p_settings.full_screen_size.y;
+ ssil.interleave_push_constant.size_modifier = uint32_t(ssil_half_size ? 4 : 2);
+
+ int interleave_pipeline = SSIL_INTERLEAVE_HALF;
+ if (ssil_quality == RS::ENV_SSIL_QUALITY_LOW) {
+ interleave_pipeline = SSIL_INTERLEAVE;
+ } else if (ssil_quality >= RS::ENV_SSIL_QUALITY_MEDIUM) {
+ interleave_pipeline = SSIL_INTERLEAVE_SMART;
+ }
+
+ shader = ssil.interleave_shader.version_get_shader(ssil.interleave_shader_version, 0);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssil.pipelines[interleave_pipeline]);
+
+ RD::Uniform u_destination(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.ssil_final }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_destination), 0);
+
+ if (ssil_quality > RS::ENV_SSIL_QUALITY_VERY_LOW && ssil_blur_passes % 2 == 0) {
+ RD::Uniform u_ssil(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.deinterleaved }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ssil), 1);
+ } else {
+ RD::Uniform u_ssil_pong(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssil_buffers.pong }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ssil_pong), 1);
+ }
+
+ RD::Uniform u_edges(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssil_buffers.edges }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_edges), 2);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssil.interleave_push_constant, sizeof(SSILInterleavePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.full_screen_size.x, p_settings.full_screen_size.y, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ RD::get_singleton()->draw_command_end_label(); // Interleave
+ }
+
+ RD::get_singleton()->draw_command_end_label(); // SSIL
+
+ RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER);
+
+ int zero[1] = { 0 };
+ RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier
+}
+
+void SSEffects::ssil_free(SSILRenderBuffers &p_ssil_buffers) {
+ if (p_ssil_buffers.ssil_final.is_valid()) {
+ RD::get_singleton()->free(p_ssil_buffers.ssil_final);
+ RD::get_singleton()->free(p_ssil_buffers.deinterleaved);
+ RD::get_singleton()->free(p_ssil_buffers.pong);
+ RD::get_singleton()->free(p_ssil_buffers.edges);
+ RD::get_singleton()->free(p_ssil_buffers.importance_map[0]);
+ RD::get_singleton()->free(p_ssil_buffers.importance_map[1]);
+ RD::get_singleton()->free(p_ssil_buffers.last_frame);
+
+ p_ssil_buffers.ssil_final = RID();
+ p_ssil_buffers.deinterleaved = RID();
+ p_ssil_buffers.pong = RID();
+ p_ssil_buffers.edges = RID();
+ p_ssil_buffers.deinterleaved_slices.clear();
+ p_ssil_buffers.pong_slices.clear();
+ p_ssil_buffers.edges_slices.clear();
+ p_ssil_buffers.importance_map[0] = RID();
+ p_ssil_buffers.importance_map[1] = RID();
+ p_ssil_buffers.last_frame = RID();
+ p_ssil_buffers.last_frame_slices.clear();
+
+ p_ssil_buffers.gather_uniform_set = RID();
+ p_ssil_buffers.importance_map_uniform_set = RID();
+ p_ssil_buffers.projection_uniform_set = RID();
+ }
+}
+
+/* SSAO */
+
+void SSEffects::ssao_set_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
+ ssao_quality = p_quality;
+ ssao_half_size = p_half_size;
+ ssao_adaptive_target = p_adaptive_target;
+ ssao_blur_passes = p_blur_passes;
+ ssao_fadeout_from = p_fadeout_from;
+ ssao_fadeout_to = p_fadeout_to;
+}
+
+void SSEffects::gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_gather_uniform_set, 0);
+ if ((ssao_quality == RS::ENV_SSAO_QUALITY_ULTRA) && !p_adaptive_base_pass) {
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, p_importance_map_uniform_set, 0);
+ }
+
+ RID shader = ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 1); //
+
+ for (int i = 0; i < 4; i++) {
+ if ((ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW) && ((i == 1) || (i == 2))) {
+ continue;
+ }
+
+ RD::Uniform u_ao_slice(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ao_slices[i] }));
+
+ ssao.gather_push_constant.pass_coord_offset[0] = i % 2;
+ ssao.gather_push_constant.pass_coord_offset[1] = i / 2;
+ ssao.gather_push_constant.pass_uv_offset[0] = ((i % 2) - 0.0) / p_settings.full_screen_size.x;
+ ssao.gather_push_constant.pass_uv_offset[1] = ((i / 2) - 0.0) / p_settings.full_screen_size.y;
+ ssao.gather_push_constant.pass = i;
+ RD::get_singleton()->compute_list_bind_uniform_set(p_compute_list, uniform_set_cache->get_cache(shader, 2, u_ao_slice), 2);
+ RD::get_singleton()->compute_list_set_push_constant(p_compute_list, &ssao.gather_push_constant, sizeof(SSAOGatherPushConstant));
+
+ Size2i size = Size2i(p_settings.full_screen_size.x >> (ssao_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssao_half_size ? 2 : 1));
+
+ RD::get_singleton()->compute_list_dispatch_threads(p_compute_list, size.x, size.y, 1);
+ }
+ RD::get_singleton()->compute_list_add_barrier(p_compute_list);
+}
+
+void SSEffects::ssao_allocate_buffers(SSAORenderBuffers &p_ssao_buffers, const SSAOSettings &p_settings, RID p_linear_depth) {
+ if (p_ssao_buffers.half_size != ssao_half_size) {
+ ssao_free(p_ssao_buffers);
+ }
+
+ if (ssao_half_size) {
+ p_ssao_buffers.buffer_width = (p_settings.full_screen_size.x + 3) / 4;
+ p_ssao_buffers.buffer_height = (p_settings.full_screen_size.y + 3) / 4;
+ p_ssao_buffers.half_buffer_width = (p_settings.full_screen_size.x + 7) / 8;
+ p_ssao_buffers.half_buffer_height = (p_settings.full_screen_size.y + 7) / 8;
+ } else {
+ p_ssao_buffers.buffer_width = (p_settings.full_screen_size.x + 1) / 2;
+ p_ssao_buffers.buffer_height = (p_settings.full_screen_size.y + 1) / 2;
+ p_ssao_buffers.half_buffer_width = (p_settings.full_screen_size.x + 3) / 4;
+ p_ssao_buffers.half_buffer_height = (p_settings.full_screen_size.y + 3) / 4;
+ }
+
+ if (p_ssao_buffers.ao_deinterleaved.is_null()) {
+ {
+ p_ssao_buffers.depth_texture_view = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_linear_depth, 0, ssao_half_size ? 1 : 0, 4, RD::TEXTURE_SLICE_2D_ARRAY);
+ }
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8G8_UNORM;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.width = p_ssao_buffers.buffer_width;
+ tf.height = p_ssao_buffers.buffer_height;
+ tf.array_layers = 4;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssao_buffers.ao_deinterleaved = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.ao_deinterleaved, "SSAO De-interleaved Array");
+ for (uint32_t i = 0; i < 4; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssao_buffers.ao_deinterleaved, i, 0);
+ p_ssao_buffers.ao_deinterleaved_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "SSAO De-interleaved Array Layer " + itos(i) + " ");
+ }
+ }
+
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8G8_UNORM;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.width = p_ssao_buffers.buffer_width;
+ tf.height = p_ssao_buffers.buffer_height;
+ tf.array_layers = 4;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssao_buffers.ao_pong = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.ao_pong, "SSAO De-interleaved Array Pong");
+ for (uint32_t i = 0; i < 4; i++) {
+ RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssao_buffers.ao_pong, i, 0);
+ p_ssao_buffers.ao_pong_slices.push_back(slice);
+ RD::get_singleton()->set_resource_name(slice, "SSAO De-interleaved Array Layer " + itos(i) + " Pong");
+ }
+ }
+
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = p_ssao_buffers.buffer_width;
+ tf.height = p_ssao_buffers.buffer_height;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssao_buffers.importance_map[0] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.importance_map[0], "SSAO Importance Map");
+ p_ssao_buffers.importance_map[1] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.importance_map[1], "SSAO Importance Map Pong");
+ }
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = p_settings.full_screen_size.x;
+ tf.height = p_settings.full_screen_size.y;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ p_ssao_buffers.ao_final = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.ao_final, "SSAO Final");
+ }
+ p_ssao_buffers.half_size = ssao_half_size;
+ }
+}
+
+void SSEffects::generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ memset(&ssao.gather_push_constant, 0, sizeof(SSAOGatherPushConstant));
+ /* FIRST PASS */
+
+ RID shader = ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 0);
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::get_singleton()->draw_command_begin_label("Process Screen Space Ambient Occlusion");
+ /* SECOND PASS */
+ // Sample SSAO
+ {
+ RD::get_singleton()->draw_command_begin_label("Gather Samples");
+ ssao.gather_push_constant.screen_size[0] = p_settings.full_screen_size.x;
+ ssao.gather_push_constant.screen_size[1] = p_settings.full_screen_size.y;
+
+ ssao.gather_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssao_buffers.buffer_width;
+ ssao.gather_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssao_buffers.buffer_height;
+ float tan_half_fov_x = 1.0 / p_projection.columns[0][0];
+ float tan_half_fov_y = 1.0 / p_projection.columns[1][1];
+ ssao.gather_push_constant.NDC_to_view_mul[0] = tan_half_fov_x * 2.0;
+ ssao.gather_push_constant.NDC_to_view_mul[1] = tan_half_fov_y * -2.0;
+ ssao.gather_push_constant.NDC_to_view_add[0] = tan_half_fov_x * -1.0;
+ ssao.gather_push_constant.NDC_to_view_add[1] = tan_half_fov_y;
+ ssao.gather_push_constant.is_orthogonal = p_projection.is_orthogonal();
+
+ ssao.gather_push_constant.half_screen_pixel_size_x025[0] = ssao.gather_push_constant.half_screen_pixel_size[0] * 0.25;
+ ssao.gather_push_constant.half_screen_pixel_size_x025[1] = ssao.gather_push_constant.half_screen_pixel_size[1] * 0.25;
+
+ ssao.gather_push_constant.radius = p_settings.radius;
+ float radius_near_limit = (p_settings.radius * 1.2f);
+ if (ssao_quality <= RS::ENV_SSAO_QUALITY_LOW) {
+ radius_near_limit *= 1.50f;
+
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW) {
+ ssao.gather_push_constant.radius *= 0.8f;
+ }
+ }
+ radius_near_limit /= tan_half_fov_y;
+ ssao.gather_push_constant.intensity = p_settings.intensity;
+ ssao.gather_push_constant.shadow_power = p_settings.power;
+ ssao.gather_push_constant.shadow_clamp = 0.98;
+ ssao.gather_push_constant.fade_out_mul = -1.0 / (ssao_fadeout_to - ssao_fadeout_from);
+ ssao.gather_push_constant.fade_out_add = ssao_fadeout_from / (ssao_fadeout_to - ssao_fadeout_from) + 1.0;
+ ssao.gather_push_constant.horizon_angle_threshold = p_settings.horizon;
+ ssao.gather_push_constant.inv_radius_near_limit = 1.0f / radius_near_limit;
+ ssao.gather_push_constant.neg_inv_radius = -1.0 / ssao.gather_push_constant.radius;
+
+ ssao.gather_push_constant.load_counter_avg_div = 9.0 / float((p_ssao_buffers.half_buffer_width) * (p_ssao_buffers.half_buffer_height) * 255);
+ ssao.gather_push_constant.adaptive_sample_limit = ssao_adaptive_target;
+
+ ssao.gather_push_constant.detail_intensity = p_settings.detail;
+ ssao.gather_push_constant.quality = MAX(0, ssao_quality - 1);
+ ssao.gather_push_constant.size_multiplier = ssao_half_size ? 2 : 1;
+
+ if (p_ssao_buffers.gather_uniform_set.is_null()) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 0;
+ u.append_id(default_sampler);
+ u.append_id(p_ssao_buffers.depth_texture_view);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 1;
+ u.append_id(p_normal_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 2;
+ u.append_id(ss_effects.gather_constants_buffer);
+ uniforms.push_back(u);
+ }
+ p_ssao_buffers.gather_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader, 0);
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.gather_uniform_set, "SSAO Gather Uniform Set");
+ }
+
+ if (p_ssao_buffers.importance_map_uniform_set.is_null()) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 0;
+ u.append_id(p_ssao_buffers.ao_pong);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 1;
+ u.append_id(default_sampler);
+ u.append_id(p_ssao_buffers.importance_map[0]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 2;
+ u.append_id(ssao.importance_map_load_counter);
+ uniforms.push_back(u);
+ }
+ p_ssao_buffers.importance_map_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, ssao.gather_shader.version_get_shader(ssao.gather_shader_version, 2), 1);
+ RD::get_singleton()->set_resource_name(p_ssao_buffers.importance_map_uniform_set, "SSAO Importance Map Uniform Set");
+ }
+
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_ULTRA) {
+ RD::get_singleton()->draw_command_begin_label("Generate Importance Map");
+ ssao.importance_map_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssao_buffers.buffer_width;
+ ssao.importance_map_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssao_buffers.buffer_height;
+ ssao.importance_map_push_constant.intensity = p_settings.intensity;
+ ssao.importance_map_push_constant.power = p_settings.power;
+
+ //base pass
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER_BASE]);
+ gather_ssao(compute_list, p_ssao_buffers.ao_pong_slices, p_settings, true, p_ssao_buffers.gather_uniform_set, RID());
+
+ //generate importance map
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GENERATE_IMPORTANCE_MAP]);
+
+ RD::Uniform u_ao_pong_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.ao_pong }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ao_pong_with_sampler), 0);
+
+ RD::Uniform u_importance_map(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssao_buffers.importance_map[0] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.importance_map_push_constant, sizeof(SSAOImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssao_buffers.half_buffer_width, p_ssao_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //process importance map A
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_PROCESS_IMPORTANCE_MAPA]);
+
+ RD::Uniform u_importance_map_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.importance_map[0] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_importance_map_with_sampler), 0);
+
+ RD::Uniform u_importance_map_pong(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssao_buffers.importance_map[1] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map_pong), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.importance_map_push_constant, sizeof(SSAOImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssao_buffers.half_buffer_width, p_ssao_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ //process Importance Map B
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_PROCESS_IMPORTANCE_MAPB]);
+
+ RD::Uniform u_importance_map_pong_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.importance_map[1] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_importance_map_pong_with_sampler), 0);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_importance_map), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, ssao.counter_uniform_set, 2);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.importance_map_push_constant, sizeof(SSAOImportanceMapPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_ssao_buffers.half_buffer_width, p_ssao_buffers.half_buffer_height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER_ADAPTIVE]);
+ RD::get_singleton()->draw_command_end_label(); // Importance Map
+ } else {
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_GATHER]);
+ }
+
+ gather_ssao(compute_list, p_ssao_buffers.ao_deinterleaved_slices, p_settings, false, p_ssao_buffers.gather_uniform_set, p_ssao_buffers.importance_map_uniform_set);
+ RD::get_singleton()->draw_command_end_label(); // Gather SSAO
+ }
+
+ // /* THIRD PASS */
+ // // Blur
+ //
+ {
+ RD::get_singleton()->draw_command_begin_label("Edge Aware Blur");
+ ssao.blur_push_constant.edge_sharpness = 1.0 - p_settings.sharpness;
+ ssao.blur_push_constant.half_screen_pixel_size[0] = 1.0 / p_ssao_buffers.buffer_width;
+ ssao.blur_push_constant.half_screen_pixel_size[1] = 1.0 / p_ssao_buffers.buffer_height;
+
+ int blur_passes = ssao_quality > RS::ENV_SSAO_QUALITY_VERY_LOW ? ssao_blur_passes : 1;
+
+ shader = ssao.blur_shader.version_get_shader(ssao.blur_shader_version, 0);
+
+ for (int pass = 0; pass < blur_passes; pass++) {
+ int blur_pipeline = SSAO_BLUR_PASS;
+ if (ssao_quality > RS::ENV_SSAO_QUALITY_VERY_LOW) {
+ blur_pipeline = SSAO_BLUR_PASS_SMART;
+ if (pass < blur_passes - 2) {
+ blur_pipeline = SSAO_BLUR_PASS_WIDE;
+ } else {
+ blur_pipeline = SSAO_BLUR_PASS_SMART;
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if ((ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW) && ((i == 1) || (i == 2))) {
+ continue;
+ }
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[blur_pipeline]);
+ if (pass % 2 == 0) {
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW) {
+ RD::Uniform u_ao_slices_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.ao_deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ao_slices_with_sampler), 0);
+ } else {
+ RD::Uniform u_ao_slices_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ ss_effects.mirror_sampler, p_ssao_buffers.ao_deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ao_slices_with_sampler), 0);
+ }
+
+ RD::Uniform u_ao_pong_slices(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssao_buffers.ao_pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ao_pong_slices), 1);
+ } else {
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_VERY_LOW) {
+ RD::Uniform u_ao_pong_slices_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.ao_pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ao_pong_slices_with_sampler), 0);
+ } else {
+ RD::Uniform u_ao_pong_slices_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ ss_effects.mirror_sampler, p_ssao_buffers.ao_pong_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_ao_pong_slices_with_sampler), 0);
+ }
+
+ RD::Uniform u_ao_slices(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssao_buffers.ao_deinterleaved_slices[i] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ao_slices), 1);
+ }
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant));
+
+ Size2i size(p_settings.full_screen_size.x >> (ssao_half_size ? 2 : 1), p_settings.full_screen_size.y >> (ssao_half_size ? 2 : 1));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, size.x, size.y, 1);
+ }
+
+ if (ssao_quality > RS::ENV_SSAO_QUALITY_VERY_LOW) {
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ }
+ }
+ RD::get_singleton()->draw_command_end_label(); // Blur
+ }
+
+ /* FOURTH PASS */
+ // Interleave buffers
+ // back to full size
+ {
+ RD::get_singleton()->draw_command_begin_label("Interleave Buffers");
+ ssao.interleave_push_constant.inv_sharpness = 1.0 - p_settings.sharpness;
+ ssao.interleave_push_constant.pixel_size[0] = 1.0 / p_settings.full_screen_size.x;
+ ssao.interleave_push_constant.pixel_size[1] = 1.0 / p_settings.full_screen_size.y;
+ ssao.interleave_push_constant.size_modifier = uint32_t(ssao_half_size ? 4 : 2);
+
+ shader = ssao.interleave_shader.version_get_shader(ssao.interleave_shader_version, 0);
+
+ int interleave_pipeline = SSAO_INTERLEAVE_HALF;
+ if (ssao_quality == RS::ENV_SSAO_QUALITY_LOW) {
+ interleave_pipeline = SSAO_INTERLEAVE;
+ } else if (ssao_quality >= RS::ENV_SSAO_QUALITY_MEDIUM) {
+ interleave_pipeline = SSAO_INTERLEAVE_SMART;
+ }
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[interleave_pipeline]);
+
+ RD::Uniform u_upscale_buffer(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssao_buffers.ao_final }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_upscale_buffer), 0);
+
+ if (ssao_quality > RS::ENV_SSAO_QUALITY_VERY_LOW && ssao_blur_passes % 2 == 0) {
+ RD::Uniform u_ao(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.ao_deinterleaved }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ao), 1);
+ } else {
+ RD::Uniform u_ao(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_ssao_buffers.ao_pong }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_ao), 1);
+ }
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.interleave_push_constant, sizeof(SSAOInterleavePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_settings.full_screen_size.x, p_settings.full_screen_size.y, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ RD::get_singleton()->draw_command_end_label(); // Interleave
+ }
+ RD::get_singleton()->draw_command_end_label(); //SSAO
+ RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //wait for upcoming transfer
+
+ int zero[1] = { 0 };
+ RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier
+}
+
+void SSEffects::ssao_free(SSAORenderBuffers &p_ssao_buffers) {
+ if (p_ssao_buffers.ao_final.is_valid()) {
+ RD::get_singleton()->free(p_ssao_buffers.ao_deinterleaved);
+ RD::get_singleton()->free(p_ssao_buffers.ao_pong);
+ RD::get_singleton()->free(p_ssao_buffers.ao_final);
+
+ RD::get_singleton()->free(p_ssao_buffers.importance_map[0]);
+ RD::get_singleton()->free(p_ssao_buffers.importance_map[1]);
+
+ p_ssao_buffers.ao_deinterleaved = RID();
+ p_ssao_buffers.ao_pong = RID();
+ p_ssao_buffers.ao_final = RID();
+ p_ssao_buffers.importance_map[0] = RID();
+ p_ssao_buffers.importance_map[1] = RID();
+ p_ssao_buffers.ao_deinterleaved_slices.clear();
+ p_ssao_buffers.ao_pong_slices.clear();
+
+ p_ssao_buffers.gather_uniform_set = RID();
+ p_ssao_buffers.importance_map_uniform_set = RID();
+ }
+}
+
+/* Screen Space Reflection */
+
+void SSEffects::ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {
+ ssr_roughness_quality = p_quality;
+}
+
+void SSEffects::ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format, const Size2i &p_screen_size, const uint32_t p_view_count) {
+ // As we are processing one view at a time, we can reuse buffers, only our output needs to have layers for each view.
+ if (p_ssr_buffers.size != p_screen_size || p_ssr_buffers.roughness_quality != ssr_roughness_quality) {
+ ssr_free(p_ssr_buffers);
+ }
+
+ if (p_ssr_buffers.output.is_valid()) {
+ // already allocated
+ return;
+ }
+
+ p_ssr_buffers.size = p_screen_size;
+ p_ssr_buffers.roughness_quality = ssr_roughness_quality;
+
+ if (p_ssr_buffers.depth_scaled.is_null()) {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+ tf.width = p_screen_size.x;
+ tf.height = p_screen_size.y;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ p_ssr_buffers.depth_scaled = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.depth_scaled, "SSR Depth Scaled");
+
+ tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+
+ p_ssr_buffers.normal_scaled = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.normal_scaled, "SSR Normal Scaled");
+ }
+
+ if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED && !p_ssr_buffers.blur_radius[0].is_valid()) {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = p_screen_size.x;
+ tf.height = p_screen_size.y;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+
+ p_ssr_buffers.blur_radius[0] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.blur_radius[0], "SSR Blur Radius 0");
+ p_ssr_buffers.blur_radius[1] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.blur_radius[1], "SSR Blur Radius 1");
+ }
+
+ if (p_ssr_buffers.intermediate.is_null()) {
+ RD::TextureFormat tf;
+ tf.format = p_color_format;
+ tf.width = p_screen_size.x;
+ tf.height = p_screen_size.y;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ p_ssr_buffers.intermediate = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.intermediate, "SSR Intermediate");
+
+ if (p_view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.array_layers = p_view_count;
+ } else {
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ }
+
+ p_ssr_buffers.output = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(p_ssr_buffers.output, "SSR Output");
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ p_ssr_buffers.output_slices[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), p_ssr_buffers.output, v, 0);
+ }
+ }
+}
+
+void SSEffects::screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, const RID *p_metallic_slices, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ {
+ // Store some scene data in a UBO, in the near future we will use a UBO shared with other shaders
+ ScreenSpaceReflectionSceneData scene_data;
+
+ if (ssr.ubo.is_null()) {
+ ssr.ubo = RD::get_singleton()->uniform_buffer_create(sizeof(ScreenSpaceReflectionSceneData));
+ }
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ store_camera(p_projections[v], scene_data.projection[v]);
+ store_camera(p_projections[v].inverse(), scene_data.inv_projection[v]);
+ scene_data.eye_offset[v][0] = p_eye_offsets[v].x;
+ scene_data.eye_offset[v][1] = p_eye_offsets[v].y;
+ scene_data.eye_offset[v][2] = p_eye_offsets[v].z;
+ scene_data.eye_offset[v][3] = 0.0;
+ }
+
+ RD::get_singleton()->buffer_update(ssr.ubo, 0, sizeof(ScreenSpaceReflectionSceneData), &scene_data, RD::BARRIER_MASK_COMPUTE);
+ }
+
+ uint32_t pipeline_specialization = 0;
+ if (p_view_count > 1) {
+ pipeline_specialization |= SSR_MULTIVIEW;
+ }
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ RD::get_singleton()->draw_command_begin_label(String("SSR View ") + itos(v));
+
+ { //scale color and depth to half
+ RD::get_singleton()->draw_command_begin_label("SSR Scale");
+
+ ScreenSpaceReflectionScalePushConstant push_constant;
+ push_constant.view_index = v;
+ push_constant.camera_z_far = p_projections[v].get_z_far();
+ push_constant.camera_z_near = p_projections[v].get_z_near();
+ push_constant.orthogonal = p_projections[v].is_orthogonal();
+ push_constant.filter = false; //enabling causes arctifacts
+ push_constant.screen_size[0] = p_screen_size.x;
+ push_constant.screen_size[1] = p_screen_size.y;
+
+ RID shader = ssr_scale.shader.version_get_shader(ssr_scale.shader_version, 0);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_scale.pipelines[pipeline_specialization]);
+
+ RD::Uniform u_diffuse(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_diffuse_slices[v] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_diffuse), 0);
+
+ RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_depth_slices[v] }));
+ RD::Uniform u_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_normal_roughness_slices[v] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth, u_normal_roughness), 1);
+
+ if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
+ RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_output), 2);
+ } else {
+ RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_intermediate), 2);
+ }
+
+ RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+ RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.normal_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth, u_scale_normal), 3);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionScalePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ RD::get_singleton()->draw_command_end_label();
+ }
+
+ {
+ RD::get_singleton()->draw_command_begin_label("SSR main");
+
+ ScreenSpaceReflectionPushConstant push_constant;
+ push_constant.view_index = v;
+ push_constant.camera_z_far = p_projections[v].get_z_far();
+ push_constant.camera_z_near = p_projections[v].get_z_near();
+ push_constant.orthogonal = p_projections[v].is_orthogonal();
+ push_constant.screen_size[0] = p_screen_size.x;
+ push_constant.screen_size[1] = p_screen_size.y;
+ push_constant.curve_fade_in = p_fade_in;
+ push_constant.distance_fade = p_fade_out;
+ push_constant.num_steps = p_max_steps;
+ push_constant.depth_tolerance = p_tolerance;
+ push_constant.use_half_res = true;
+ push_constant.proj_info[0] = -2.0f / (p_screen_size.width * p_projections[v].columns[0][0]);
+ push_constant.proj_info[1] = -2.0f / (p_screen_size.height * p_projections[v].columns[1][1]);
+ push_constant.proj_info[2] = (1.0f - p_projections[v].columns[0][2]) / p_projections[v].columns[0][0];
+ push_constant.proj_info[3] = (1.0f + p_projections[v].columns[1][2]) / p_projections[v].columns[1][1];
+
+ ScreenSpaceReflectionMode mode = (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) ? SCREEN_SPACE_REFLECTION_ROUGH : SCREEN_SPACE_REFLECTION_NORMAL;
+ RID shader = ssr.shader.version_get_shader(ssr.shader_version, mode);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr.pipelines[pipeline_specialization][mode]);
+
+ RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 0, ssr.ubo);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
+
+ if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
+ // read from output slices (our scale wrote into these)
+ RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+ RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_output, u_scale_depth), 0);
+
+ // write to intermediate (our roughness pass will output into output slices)
+ RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
+ RD::Uniform u_blur_radius(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.blur_radius[0] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_intermediate, u_blur_radius), 1);
+ } else {
+ // read from intermediate (our scale wrote into these)
+ RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
+ RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate, u_scale_depth), 0);
+
+ // We are not performing our blur so go directly to output.
+ RD::Uniform u_output(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_output), 1);
+ }
+
+ RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.normal_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_scale_normal), 2);
+
+ RD::Uniform u_metallic(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_metallic_slices[v] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_metallic), 3);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+
+ RD::get_singleton()->draw_command_end_label();
+ }
+
+ if (ssr_roughness_quality != RS::ENV_SSR_ROUGHNESS_QUALITY_DISABLED) {
+ RD::get_singleton()->draw_command_begin_label("SSR filter");
+ //blur
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ ScreenSpaceReflectionFilterPushConstant push_constant;
+ push_constant.view_index = v;
+ push_constant.orthogonal = p_projections[v].is_orthogonal();
+ push_constant.edge_tolerance = Math::sin(Math::deg_to_rad(15.0));
+ push_constant.proj_info[0] = -2.0f / (p_screen_size.width * p_projections[v].columns[0][0]);
+ push_constant.proj_info[1] = -2.0f / (p_screen_size.height * p_projections[v].columns[1][1]);
+ push_constant.proj_info[2] = (1.0f - p_projections[v].columns[0][2]) / p_projections[v].columns[0][0];
+ push_constant.proj_info[3] = (1.0f + p_projections[v].columns[1][2]) / p_projections[v].columns[1][1];
+ push_constant.vertical = 0;
+ if (ssr_roughness_quality == RS::ENV_SSR_ROUGHNESS_QUALITY_LOW) {
+ push_constant.steps = p_max_steps / 3;
+ push_constant.increment = 3;
+ } else if (ssr_roughness_quality == RS::ENV_SSR_ROUGHNESS_QUALITY_MEDIUM) {
+ push_constant.steps = p_max_steps / 2;
+ push_constant.increment = 2;
+ } else {
+ push_constant.steps = p_max_steps;
+ push_constant.increment = 1;
+ }
+
+ push_constant.screen_size[0] = p_screen_size.width;
+ push_constant.screen_size[1] = p_screen_size.height;
+
+ // Horizontal pass
+
+ SSRReflectionMode mode = SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL;
+
+ RID shader = ssr_filter.shader.version_get_shader(ssr_filter.shader_version, mode);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_filter.pipelines[pipeline_specialization][mode]);
+
+ RD::Uniform u_scene_data(RD::UNIFORM_TYPE_UNIFORM_BUFFER, 0, ssr.ubo);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
+
+ RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.intermediate }));
+ RD::Uniform u_blur_radius(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.blur_radius[0] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate, u_blur_radius), 0);
+
+ RD::Uniform u_scale_normal(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.normal_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_scale_normal), 1);
+
+ RD::Uniform u_output_blur(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.output_slices[v] }));
+ RD::Uniform u_blur_radius2(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_ssr_buffers.blur_radius[1] }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_output_blur, u_blur_radius2), 2);
+
+ RD::Uniform u_scale_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_ssr_buffers.depth_scaled }));
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth), 3);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionFilterPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ // Vertical pass
+
+ mode = SCREEN_SPACE_REFLECTION_FILTER_VERTICAL;
+ shader = ssr_filter.shader.version_get_shader(ssr_filter.shader_version, mode);
+
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_filter.pipelines[pipeline_specialization][mode]);
+
+ push_constant.vertical = 1;
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_output_blur, u_blur_radius2), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_scale_normal), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_intermediate), 2);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_scale_depth), 3);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 4, u_scene_data), 4);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ScreenSpaceReflectionFilterPushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+
+ if (v != p_view_count - 1) {
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+ }
+
+ RD::get_singleton()->compute_list_end();
+}
+
+void SSEffects::ssr_free(SSRRenderBuffers &p_ssr_buffers) {
+ for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) {
+ p_ssr_buffers.output_slices[v] = RID();
+ }
+
+ if (p_ssr_buffers.output.is_valid()) {
+ RD::get_singleton()->free(p_ssr_buffers.output);
+ p_ssr_buffers.output = RID();
+ }
+
+ if (p_ssr_buffers.intermediate.is_valid()) {
+ RD::get_singleton()->free(p_ssr_buffers.intermediate);
+ p_ssr_buffers.intermediate = RID();
+ }
+
+ if (p_ssr_buffers.blur_radius[0].is_valid()) {
+ RD::get_singleton()->free(p_ssr_buffers.blur_radius[0]);
+ RD::get_singleton()->free(p_ssr_buffers.blur_radius[1]);
+ p_ssr_buffers.blur_radius[0] = RID();
+ p_ssr_buffers.blur_radius[1] = RID();
+ }
+
+ if (p_ssr_buffers.depth_scaled.is_valid()) {
+ RD::get_singleton()->free(p_ssr_buffers.depth_scaled);
+ p_ssr_buffers.depth_scaled = RID();
+ RD::get_singleton()->free(p_ssr_buffers.normal_scaled);
+ p_ssr_buffers.normal_scaled = RID();
+ }
+}
+
+/* Subsurface scattering */
+
+void SSEffects::sss_set_quality(RS::SubSurfaceScatteringQuality p_quality) {
+ sss_quality = p_quality;
+}
+
+RS::SubSurfaceScatteringQuality SSEffects::sss_get_quality() const {
+ return sss_quality;
+}
+
+void SSEffects::sss_set_scale(float p_scale, float p_depth_scale) {
+ sss_scale = p_scale;
+ sss_depth_scale = p_depth_scale;
+}
+
+void SSEffects::sub_surface_scattering(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_diffuse, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ // Our intermediate buffer is only created if we haven't created it already.
+ RD::DataFormat format = p_render_buffers->get_base_data_format();
+ uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ uint32_t layers = 1; // We only need one layer, we're handling one view at a time
+ uint32_t mipmaps = 1; // Image::get_image_required_mipmaps(p_screen_size.x, p_screen_size.y, Image::FORMAT_RGBAH);
+ RID intermediate = p_render_buffers->create_texture(SNAME("SSR"), SNAME("intermediate"), format, usage_bits, RD::TEXTURE_SAMPLES_1, p_screen_size, layers, mipmaps);
+
+ Plane p = p_camera.xform4(Plane(1, 0, -1, 1));
+ p.normal /= p.d;
+ float unit_size = p.normal.x;
+
+ { //scale color and depth to half
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+ sss.push_constant.camera_z_far = p_camera.get_z_far();
+ sss.push_constant.camera_z_near = p_camera.get_z_near();
+ sss.push_constant.orthogonal = p_camera.is_orthogonal();
+ sss.push_constant.unit_size = unit_size;
+ sss.push_constant.screen_size[0] = p_screen_size.x;
+ sss.push_constant.screen_size[1] = p_screen_size.y;
+ sss.push_constant.vertical = false;
+ sss.push_constant.scale = sss_scale;
+ sss.push_constant.depth_scale = sss_depth_scale;
+
+ RID shader = sss.shader.version_get_shader(sss.shader_version, sss_quality - 1);
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sss.pipelines[sss_quality - 1]);
+
+ RD::Uniform u_diffuse_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_diffuse }));
+ RD::Uniform u_diffuse(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_diffuse }));
+ RD::Uniform u_intermediate_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, intermediate }));
+ RD::Uniform u_intermediate(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ intermediate }));
+ RD::Uniform u_depth_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_depth }));
+
+ // horizontal
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_diffuse_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_intermediate), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_depth_with_sampler), 2);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+
+ RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+ // vertical
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_intermediate_with_sampler), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_diffuse), 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_depth_with_sampler), 2);
+
+ sss.push_constant.vertical = true;
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &sss.push_constant, sizeof(SubSurfaceScatteringPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.width, p_screen_size.height, 1);
+
+ RD::get_singleton()->compute_list_end();
+ }
+}
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.h b/servers/rendering/renderer_rd/effects/ss_effects.h
new file mode 100644
index 0000000000..dfaf3881bb
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/ss_effects.h
@@ -0,0 +1,552 @@
+/*************************************************************************/
+/* ss_effects.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SS_EFFECTS_RD_H
+#define SS_EFFECTS_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssao.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssao_importance_map.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssao_interleave.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssil.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssil_blur.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssil_importance_map.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/ssil_interleave.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering_server.h"
+
+class RenderSceneBuffersRD;
+
+namespace RendererRD {
+
+class SSEffects {
+private:
+ static SSEffects *singleton;
+
+public:
+ static SSEffects *get_singleton() { return singleton; }
+
+ SSEffects();
+ ~SSEffects();
+
+ /* SS Downsampler */
+
+ void downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_mipmaps, bool p_invalidate_uniform_set, Size2i p_full_screen_size, const Projection &p_projection);
+
+ /* SSIL */
+ void ssil_set_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to);
+
+ struct SSILRenderBuffers {
+ bool half_size = false;
+ int buffer_width;
+ int buffer_height;
+ int half_buffer_width;
+ int half_buffer_height;
+
+ RID ssil_final;
+ RID deinterleaved;
+ Vector<RID> deinterleaved_slices;
+ RID pong;
+ Vector<RID> pong_slices;
+ RID edges;
+ Vector<RID> edges_slices;
+ RID importance_map[2];
+ RID depth_texture_view;
+
+ RID last_frame;
+ Vector<RID> last_frame_slices;
+
+ RID gather_uniform_set;
+ RID importance_map_uniform_set;
+ RID projection_uniform_set;
+ };
+
+ struct SSILSettings {
+ float radius = 1.0;
+ float intensity = 2.0;
+ float sharpness = 0.98;
+ float normal_rejection = 1.0;
+
+ Size2i full_screen_size = Size2i();
+ };
+
+ void ssil_allocate_buffers(SSILRenderBuffers &p_ssil_buffers, const SSILSettings &p_settings, RID p_linear_depth);
+ void screen_space_indirect_lighting(SSILRenderBuffers &p_ssil_buffers, RID p_normal_buffer, const Projection &p_projection, const Projection &p_last_projection, const SSILSettings &p_settings);
+ void ssil_free(SSILRenderBuffers &p_ssil_buffers);
+
+ /* SSAO */
+ void ssao_set_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to);
+
+ struct SSAORenderBuffers {
+ bool half_size = false;
+ int buffer_width;
+ int buffer_height;
+ int half_buffer_width;
+ int half_buffer_height;
+
+ RID ao_deinterleaved;
+ Vector<RID> ao_deinterleaved_slices;
+ RID ao_pong;
+ Vector<RID> ao_pong_slices;
+ RID ao_final;
+ RID importance_map[2];
+ RID depth_texture_view;
+
+ RID gather_uniform_set;
+ RID importance_map_uniform_set;
+ };
+
+ struct SSAOSettings {
+ float radius = 1.0;
+ float intensity = 2.0;
+ float power = 1.5;
+ float detail = 0.5;
+ float horizon = 0.06;
+ float sharpness = 0.98;
+
+ Size2i full_screen_size = Size2i();
+ };
+
+ void ssao_allocate_buffers(SSAORenderBuffers &p_ssao_buffers, const SSAOSettings &p_settings, RID p_linear_depth);
+ void generate_ssao(SSAORenderBuffers &p_ssao_buffers, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings);
+ void ssao_free(SSAORenderBuffers &p_ssao_buffers);
+
+ /* Screen Space Reflection */
+ void ssr_set_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality);
+
+ struct SSRRenderBuffers {
+ Size2i size;
+ RenderingServer::EnvironmentSSRRoughnessQuality roughness_quality = RenderingServer::ENV_SSR_ROUGHNESS_QUALITY_DISABLED;
+
+ RID normal_scaled;
+ RID depth_scaled;
+ RID blur_radius[2];
+ RID intermediate;
+ RID output;
+ RID output_slices[RendererSceneRender::MAX_RENDER_VIEWS];
+ };
+
+ void ssr_allocate_buffers(SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format, const Size2i &p_screen_size, const uint32_t p_view_count);
+ void screen_space_reflection(SSRRenderBuffers &p_ssr_buffers, const RID *p_diffuse_slices, const RID *p_normal_roughness_slices, const RID *p_metallic_slices, const RID *p_depth_slices, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets);
+ void ssr_free(SSRRenderBuffers &p_ssr_buffers);
+
+ /* subsurface scattering */
+ void sss_set_quality(RS::SubSurfaceScatteringQuality p_quality);
+ RS::SubSurfaceScatteringQuality sss_get_quality() const;
+ void sss_set_scale(float p_scale, float p_depth_scale);
+
+ void sub_surface_scattering(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_diffuse, RID p_depth, const Projection &p_camera, const Size2i &p_screen_size);
+
+private:
+ /* Settings */
+
+ RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM;
+ bool ssao_half_size = false;
+ float ssao_adaptive_target = 0.5;
+ int ssao_blur_passes = 2;
+ float ssao_fadeout_from = 50.0;
+ float ssao_fadeout_to = 300.0;
+
+ RS::EnvironmentSSILQuality ssil_quality = RS::ENV_SSIL_QUALITY_MEDIUM;
+ bool ssil_half_size = false;
+ float ssil_adaptive_target = 0.5;
+ int ssil_blur_passes = 4;
+ float ssil_fadeout_from = 50.0;
+ float ssil_fadeout_to = 300.0;
+
+ RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
+
+ RS::SubSurfaceScatteringQuality sss_quality = RS::SUB_SURFACE_SCATTERING_QUALITY_MEDIUM;
+ float sss_scale = 0.05;
+ float sss_depth_scale = 0.01;
+
+ /* SS Downsampler */
+
+ struct SSEffectsDownsamplePushConstant {
+ float pixel_size[2];
+ float z_far;
+ float z_near;
+ uint32_t orthogonal;
+ float radius_sq;
+ uint32_t pad[2];
+ };
+
+ enum SSEffectsMode {
+ SS_EFFECTS_DOWNSAMPLE,
+ SS_EFFECTS_DOWNSAMPLE_HALF_RES,
+ SS_EFFECTS_DOWNSAMPLE_MIPMAP,
+ SS_EFFECTS_DOWNSAMPLE_MIPMAP_HALF_RES,
+ SS_EFFECTS_DOWNSAMPLE_HALF,
+ SS_EFFECTS_DOWNSAMPLE_HALF_RES_HALF,
+ SS_EFFECTS_DOWNSAMPLE_FULL_MIPS,
+ SS_EFFECTS_MAX
+ };
+
+ struct SSEffectsGatherConstants {
+ float rotation_matrices[80]; //5 vec4s * 4
+ };
+
+ struct SSEffectsShader {
+ SSEffectsDownsamplePushConstant downsample_push_constant;
+ SsEffectsDownsampleShaderRD downsample_shader;
+ RID downsample_shader_version;
+ RID downsample_uniform_set;
+ bool used_half_size_last_frame = false;
+ bool used_mips_last_frame = false;
+ bool used_full_mips_last_frame = false;
+
+ RID gather_constants_buffer;
+
+ RID mirror_sampler;
+
+ RID pipelines[SS_EFFECTS_MAX];
+ } ss_effects;
+
+ /* SSIL */
+
+ enum SSILMode {
+ SSIL_GATHER,
+ SSIL_GATHER_BASE,
+ SSIL_GATHER_ADAPTIVE,
+ SSIL_GENERATE_IMPORTANCE_MAP,
+ SSIL_PROCESS_IMPORTANCE_MAPA,
+ SSIL_PROCESS_IMPORTANCE_MAPB,
+ SSIL_BLUR_PASS,
+ SSIL_BLUR_PASS_SMART,
+ SSIL_BLUR_PASS_WIDE,
+ SSIL_INTERLEAVE,
+ SSIL_INTERLEAVE_SMART,
+ SSIL_INTERLEAVE_HALF,
+ SSIL_MAX
+ };
+
+ struct SSILGatherPushConstant {
+ int32_t screen_size[2];
+ int pass;
+ int quality;
+
+ float half_screen_pixel_size[2];
+ float half_screen_pixel_size_x025[2];
+
+ float NDC_to_view_mul[2];
+ float NDC_to_view_add[2];
+
+ float pad2[2];
+ float z_near;
+ float z_far;
+
+ float radius;
+ float intensity;
+ int size_multiplier;
+ int pad;
+
+ float fade_out_mul;
+ float fade_out_add;
+ float normal_rejection_amount;
+ float inv_radius_near_limit;
+
+ uint32_t is_orthogonal;
+ float neg_inv_radius;
+ float load_counter_avg_div;
+ float adaptive_sample_limit;
+
+ int32_t pass_coord_offset[2];
+ float pass_uv_offset[2];
+ };
+
+ struct SSILImportanceMapPushConstant {
+ float half_screen_pixel_size[2];
+ float intensity;
+ float pad;
+ };
+
+ struct SSILBlurPushConstant {
+ float edge_sharpness;
+ float pad;
+ float half_screen_pixel_size[2];
+ };
+
+ struct SSILInterleavePushConstant {
+ float inv_sharpness;
+ uint32_t size_modifier;
+ float pixel_size[2];
+ };
+
+ struct SSILProjectionUniforms {
+ float inv_last_frame_projection_matrix[16];
+ };
+
+ struct SSIL {
+ SSILGatherPushConstant gather_push_constant;
+ SsilShaderRD gather_shader;
+ RID gather_shader_version;
+ RID projection_uniform_buffer;
+
+ SSILImportanceMapPushConstant importance_map_push_constant;
+ SsilImportanceMapShaderRD importance_map_shader;
+ RID importance_map_shader_version;
+ RID importance_map_load_counter;
+ RID counter_uniform_set;
+
+ SSILBlurPushConstant blur_push_constant;
+ SsilBlurShaderRD blur_shader;
+ RID blur_shader_version;
+
+ SSILInterleavePushConstant interleave_push_constant;
+ SsilInterleaveShaderRD interleave_shader;
+ RID interleave_shader_version;
+
+ RID pipelines[SSIL_MAX];
+ } ssil;
+
+ void gather_ssil(RD::ComputeListID p_compute_list, const Vector<RID> p_ssil_slices, const Vector<RID> p_edges_slices, const SSILSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set, RID p_projection_uniform_set);
+
+ /* SSAO */
+
+ enum SSAOMode {
+ SSAO_GATHER,
+ SSAO_GATHER_BASE,
+ SSAO_GATHER_ADAPTIVE,
+ SSAO_GENERATE_IMPORTANCE_MAP,
+ SSAO_PROCESS_IMPORTANCE_MAPA,
+ SSAO_PROCESS_IMPORTANCE_MAPB,
+ SSAO_BLUR_PASS,
+ SSAO_BLUR_PASS_SMART,
+ SSAO_BLUR_PASS_WIDE,
+ SSAO_INTERLEAVE,
+ SSAO_INTERLEAVE_SMART,
+ SSAO_INTERLEAVE_HALF,
+ SSAO_MAX
+ };
+
+ struct SSAOGatherPushConstant {
+ int32_t screen_size[2];
+ int pass;
+ int quality;
+
+ float half_screen_pixel_size[2];
+ int size_multiplier;
+ float detail_intensity;
+
+ float NDC_to_view_mul[2];
+ float NDC_to_view_add[2];
+
+ float pad[2];
+ float half_screen_pixel_size_x025[2];
+
+ float radius;
+ float intensity;
+ float shadow_power;
+ float shadow_clamp;
+
+ float fade_out_mul;
+ float fade_out_add;
+ float horizon_angle_threshold;
+ float inv_radius_near_limit;
+
+ uint32_t is_orthogonal;
+ float neg_inv_radius;
+ float load_counter_avg_div;
+ float adaptive_sample_limit;
+
+ int32_t pass_coord_offset[2];
+ float pass_uv_offset[2];
+ };
+
+ struct SSAOImportanceMapPushConstant {
+ float half_screen_pixel_size[2];
+ float intensity;
+ float power;
+ };
+
+ struct SSAOBlurPushConstant {
+ float edge_sharpness;
+ float pad;
+ float half_screen_pixel_size[2];
+ };
+
+ struct SSAOInterleavePushConstant {
+ float inv_sharpness;
+ uint32_t size_modifier;
+ float pixel_size[2];
+ };
+
+ struct SSAO {
+ SSAOGatherPushConstant gather_push_constant;
+ SsaoShaderRD gather_shader;
+ RID gather_shader_version;
+
+ SSAOImportanceMapPushConstant importance_map_push_constant;
+ SsaoImportanceMapShaderRD importance_map_shader;
+ RID importance_map_shader_version;
+ RID importance_map_load_counter;
+ RID counter_uniform_set;
+
+ SSAOBlurPushConstant blur_push_constant;
+ SsaoBlurShaderRD blur_shader;
+ RID blur_shader_version;
+
+ SSAOInterleavePushConstant interleave_push_constant;
+ SsaoInterleaveShaderRD interleave_shader;
+ RID interleave_shader_version;
+
+ RID pipelines[SSAO_MAX];
+ } ssao;
+
+ void gather_ssao(RD::ComputeListID p_compute_list, const Vector<RID> p_ao_slices, const SSAOSettings &p_settings, bool p_adaptive_base_pass, RID p_gather_uniform_set, RID p_importance_map_uniform_set);
+
+ /* Screen Space Reflection */
+
+ enum SSRShaderSpecializations {
+ SSR_MULTIVIEW = 1 << 0,
+ SSR_VARIATIONS = 2,
+ };
+
+ struct ScreenSpaceReflectionSceneData {
+ float projection[2][16];
+ float inv_projection[2][16];
+ float eye_offset[2][4];
+ };
+
+ // SSR Scale
+
+ struct ScreenSpaceReflectionScalePushConstant {
+ int32_t screen_size[2];
+ float camera_z_near;
+ float camera_z_far;
+
+ uint32_t orthogonal;
+ uint32_t filter;
+ uint32_t view_index;
+ uint32_t pad1;
+ };
+
+ struct ScreenSpaceReflectionScale {
+ ScreenSpaceReflectionScaleShaderRD shader;
+ RID shader_version;
+ RID pipelines[SSR_VARIATIONS];
+ } ssr_scale;
+
+ // SSR main
+
+ enum ScreenSpaceReflectionMode {
+ SCREEN_SPACE_REFLECTION_NORMAL,
+ SCREEN_SPACE_REFLECTION_ROUGH,
+ SCREEN_SPACE_REFLECTION_MAX,
+ };
+
+ struct ScreenSpaceReflectionPushConstant {
+ float proj_info[4]; // 16 - 16
+
+ int32_t screen_size[2]; // 8 - 24
+ float camera_z_near; // 4 - 28
+ float camera_z_far; // 4 - 32
+
+ int32_t num_steps; // 4 - 36
+ float depth_tolerance; // 4 - 40
+ float distance_fade; // 4 - 44
+ float curve_fade_in; // 4 - 48
+
+ uint32_t orthogonal; // 4 - 52
+ float filter_mipmap_levels; // 4 - 56
+ uint32_t use_half_res; // 4 - 60
+ uint32_t view_index; // 4 - 64
+
+ // float projection[16]; // this is in our ScreenSpaceReflectionSceneData now
+ };
+
+ struct ScreenSpaceReflection {
+ ScreenSpaceReflectionShaderRD shader;
+ RID shader_version;
+ RID pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_MAX];
+
+ RID ubo;
+ } ssr;
+
+ // SSR Filter
+
+ struct ScreenSpaceReflectionFilterPushConstant {
+ float proj_info[4]; // 16 - 16
+
+ uint32_t orthogonal; // 4 - 20
+ float edge_tolerance; // 4 - 24
+ int32_t increment; // 4 - 28
+ uint32_t view_index; // 4 - 32
+
+ int32_t screen_size[2]; // 8 - 40
+ uint32_t vertical; // 4 - 44
+ uint32_t steps; // 4 - 48
+ };
+
+ enum SSRReflectionMode {
+ SCREEN_SPACE_REFLECTION_FILTER_HORIZONTAL,
+ SCREEN_SPACE_REFLECTION_FILTER_VERTICAL,
+ SCREEN_SPACE_REFLECTION_FILTER_MAX,
+ };
+
+ struct ScreenSpaceReflectionFilter {
+ ScreenSpaceReflectionFilterShaderRD shader;
+ RID shader_version;
+ RID pipelines[SSR_VARIATIONS][SCREEN_SPACE_REFLECTION_FILTER_MAX];
+ } ssr_filter;
+
+ /* Subsurface scattering */
+
+ struct SubSurfaceScatteringPushConstant {
+ int32_t screen_size[2];
+ float camera_z_far;
+ float camera_z_near;
+
+ uint32_t vertical;
+ uint32_t orthogonal;
+ float unit_size;
+ float scale;
+
+ float depth_scale;
+ uint32_t pad[3];
+ };
+
+ struct SubSurfaceScattering {
+ SubSurfaceScatteringPushConstant push_constant;
+ SubsurfaceScatteringShaderRD shader;
+ RID shader_version;
+ RID pipelines[3]; //3 quality levels
+ } sss;
+};
+
+} // namespace RendererRD
+
+#endif // SS_EFFECTS_RD_H
diff --git a/servers/rendering/renderer_rd/effects/taa.cpp b/servers/rendering/renderer_rd/effects/taa.cpp
new file mode 100644
index 0000000000..657385a509
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/taa.cpp
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* taa.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "taa.h"
+#include "servers/rendering/renderer_rd/effects/copy_effects.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+TAA::TAA() {
+ Vector<String> taa_modes;
+ taa_modes.push_back("\n#define MODE_TAA_RESOLVE");
+ taa_shader.initialize(taa_modes);
+ shader_version = taa_shader.version_create();
+ pipeline = RD::get_singleton()->compute_pipeline_create(taa_shader.version_get_shader(shader_version, 0));
+}
+
+TAA::~TAA() {
+ taa_shader.version_free(shader_version);
+}
+
+void TAA::msaa_resolve(Ref<RenderSceneBuffersRD> p_render_buffers) {
+ if (!p_render_buffers->has_velocity_buffer(true)) {
+ // nothing to resolve
+ return;
+ }
+
+ for (uint32_t v = 0; v < p_render_buffers->get_view_count(); v++) {
+ RID velocity_buffer_msaa = p_render_buffers->get_velocity_buffer(true, v);
+ RID velocity_buffer = p_render_buffers->get_velocity_buffer(false, v);
+
+ RD::get_singleton()->texture_resolve_multisample(velocity_buffer_msaa, velocity_buffer);
+ }
+}
+
+void TAA::resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ RID shader = taa_shader.version_get_shader(shader_version, 0);
+ ERR_FAIL_COND(shader.is_null());
+
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ TAAResolvePushConstant push_constant;
+ memset(&push_constant, 0, sizeof(TAAResolvePushConstant));
+ push_constant.resolution_width = p_resolution.width;
+ push_constant.resolution_height = p_resolution.height;
+ push_constant.disocclusion_threshold = 0.025f;
+ push_constant.disocclusion_scale = 10.0f;
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
+
+ RD::Uniform u_frame_source(RD::UNIFORM_TYPE_IMAGE, 0, { p_frame });
+ RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, { default_sampler, p_depth });
+ RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 2, { p_velocity });
+ RD::Uniform u_prev_velocity(RD::UNIFORM_TYPE_IMAGE, 3, { p_prev_velocity });
+ RD::Uniform u_history(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 4, { default_sampler, p_history });
+ RD::Uniform u_frame_dest(RD::UNIFORM_TYPE_IMAGE, 5, { p_temp });
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_frame_source, u_depth, u_velocity, u_prev_velocity, u_history, u_frame_dest), 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(TAAResolvePushConstant));
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_resolution.width, p_resolution.height, 1);
+ RD::get_singleton()->compute_list_end();
+}
+
+void TAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far) {
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+
+ uint32_t view_count = p_render_buffers->get_view_count();
+ Size2i internal_size = p_render_buffers->get_internal_size();
+ Size2i target_size = p_render_buffers->get_target_size();
+
+ bool just_allocated = false;
+ if (!p_render_buffers->has_texture(SNAME("taa"), SNAME("history"))) {
+ uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+
+ p_render_buffers->create_texture(SNAME("taa"), SNAME("history"), p_format, usage_bits);
+ p_render_buffers->create_texture(SNAME("taa"), SNAME("temp"), p_format, usage_bits);
+
+ p_render_buffers->create_texture(SNAME("taa"), SNAME("prev_velocity"), RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits);
+
+ just_allocated = true;
+ }
+
+ RD::get_singleton()->draw_command_begin_label("TAA");
+
+ for (uint32_t v = 0; v < view_count; v++) {
+ // Get our (cached) slices
+ RID internal_texture = p_render_buffers->get_internal_texture(v);
+ RID velocity_buffer = p_render_buffers->get_velocity_buffer(false, v);
+ RID taa_history = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("history"), v, 0);
+ RID taa_prev_velocity = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("prev_velocity"), v, 0);
+
+ if (!just_allocated) {
+ RID depth_texture = p_render_buffers->get_depth_texture(v);
+ RID taa_temp = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("temp"), v, 0);
+ resolve(internal_texture, taa_temp, depth_texture, velocity_buffer, taa_prev_velocity, taa_history, Size2(internal_size.x, internal_size.y), p_z_near, p_z_far);
+ copy_effects->copy_to_rect(taa_temp, internal_texture, Rect2(0, 0, internal_size.x, internal_size.y));
+ }
+
+ copy_effects->copy_to_rect(internal_texture, taa_history, Rect2(0, 0, internal_size.x, internal_size.y));
+ copy_effects->copy_to_rect(velocity_buffer, taa_prev_velocity, Rect2(0, 0, target_size.x, target_size.y));
+ }
+
+ RD::get_singleton()->draw_command_end_label();
+}
diff --git a/servers/rendering/renderer_rd/effects/taa.h b/servers/rendering/renderer_rd/effects/taa.h
new file mode 100644
index 0000000000..ce4af18866
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/taa.h
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* taa.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TAA_RD_H
+#define TAA_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl.gen.h"
+#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class TAA {
+public:
+ TAA();
+ ~TAA();
+
+ void msaa_resolve(Ref<RenderSceneBuffersRD> p_render_buffers);
+ void process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far);
+
+private:
+ struct TAAResolvePushConstant {
+ float resolution_width;
+ float resolution_height;
+ float disocclusion_threshold;
+ float disocclusion_scale;
+ };
+
+ TaaResolveShaderRD taa_shader;
+ RID shader_version;
+ RID pipeline;
+
+ void resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far);
+};
+
+} // namespace RendererRD
+
+#endif // TAA_RD_H
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
index 38a4a37b8a..3a47b1420b 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp
@@ -117,7 +117,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton
tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
- tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey;
+ tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
@@ -203,7 +203,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col
tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
- tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey;
+ tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.use_color_correction = p_settings.use_color_correction;
diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h
index a90849dbeb..e91118e241 100644
--- a/servers/rendering/renderer_rd/effects/tone_mapper.h
+++ b/servers/rendering/renderer_rd/effects/tone_mapper.h
@@ -77,7 +77,7 @@ private:
float exposure; // 4 - 84
float white; // 4 - 88
- float auto_exposure_grey; // 4 - 92
+ float auto_exposure_scale; // 4 - 92
float luminance_multiplier; // 4 - 96
float pixel_size[2]; // 8 - 104
@@ -124,7 +124,7 @@ public:
float white = 1.0;
bool use_auto_exposure = false;
- float auto_exposure_grey = 0.5;
+ float auto_exposure_scale = 0.5;
RID exposure_texture;
float luminance_multiplier = 1.0;
@@ -149,4 +149,4 @@ public:
} // namespace RendererRD
-#endif // !TONE_MAPPER_RD_H
+#endif // TONE_MAPPER_RD_H
diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp
index 505a35a269..5ff00aa94c 100644
--- a/servers/rendering/renderer_rd/effects/vrs.cpp
+++ b/servers/rendering/renderer_rd/effects/vrs.cpp
@@ -91,47 +91,22 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
RD::get_singleton()->draw_list_end();
}
-void VRS::create_vrs_texture(const int p_base_width, const int p_base_height, const uint32_t p_view_count, RID &p_vrs_texture, RID &p_vrs_fb) {
- // TODO find a way to skip this if VRS is not supported, but we don't have access to VulkanContext here, even though we're in vulkan.. hmmm
-
+Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const {
// TODO we should find some way to store this properly, we're assuming 16x16 as this seems to be the standard but in our vrs_capacities we
- // obtain a minimum and maximum size, and we should choose something within this range and then make sure that is consistantly set when creating
+ // obtain a minimum and maximum size, and we should choose something within this range and then make sure that is consistently set when creating
// our frame buffer. Also it is important that we make the resulting size we calculate down below available to the end user so they know the size
// of the VRS buffer to supply.
Size2i texel_size = Size2i(16, 16);
- RD::TextureFormat tf;
- if (p_view_count > 1) {
- tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
- } else {
- tf.texture_type = RD::TEXTURE_TYPE_2D;
- }
- tf.format = RD::DATA_FORMAT_R8_UINT;
- tf.width = p_base_width / texel_size.x;
- if (p_base_width % texel_size.x != 0) {
- tf.width++;
+ int width = p_base_size.x / texel_size.x;
+ if (p_base_size.x % texel_size.x != 0) {
+ width++;
}
- tf.height = p_base_height / texel_size.y;
- if (p_base_height % texel_size.y != 0) {
- tf.height++;
+ int height = p_base_size.y / texel_size.y;
+ if (p_base_size.y % texel_size.y != 0) {
+ height++;
}
- tf.array_layers = p_view_count; // create a layer for every view
- tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- tf.samples = RD::TEXTURE_SAMPLES_1;
-
- p_vrs_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
-
- // by default VRS is assumed to be our VRS attachment, but if we need to write into it, we need a bit more control
- Vector<RID> fb;
- fb.push_back(p_vrs_texture);
-
- RD::FramebufferPass pass;
- pass.color_attachments.push_back(0);
-
- Vector<RD::FramebufferPass> passes;
- passes.push_back(pass);
-
- p_vrs_fb = RD::get_singleton()->framebuffer_create_multipass(fb, passes, RenderingDevice::INVALID_ID, p_view_count);
+ return Size2i(width, height);
}
void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
@@ -146,10 +121,11 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
if (vrs_mode == RS::VIEWPORT_VRS_TEXTURE) {
RID vrs_texture = texture_storage->render_target_get_vrs_texture(p_render_target);
if (vrs_texture.is_valid()) {
- Texture *texture = texture_storage->get_texture(vrs_texture);
- if (texture) {
+ RID rd_texture = texture_storage->texture_get_rd_texture(vrs_texture);
+ int layers = texture_storage->texture_get_layers(vrs_texture);
+ if (rd_texture.is_valid()) {
// Copy into our density buffer
- copy_vrs(texture->rd_texture, p_vrs_fb, texture->layers > 1);
+ copy_vrs(rd_texture, p_vrs_fb, layers > 1);
}
}
} else if (vrs_mode == RS::VIEWPORT_VRS_XR) {
@@ -157,10 +133,12 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
if (interface.is_valid()) {
RID vrs_texture = interface->get_vrs_texture();
if (vrs_texture.is_valid()) {
- Texture *texture = texture_storage->get_texture(vrs_texture);
- if (texture) {
+ RID rd_texture = texture_storage->texture_get_rd_texture(vrs_texture);
+ int layers = texture_storage->texture_get_layers(vrs_texture);
+
+ if (rd_texture.is_valid()) {
// Copy into our density buffer
- copy_vrs(texture->rd_texture, p_vrs_fb, texture->layers > 1);
+ copy_vrs(rd_texture, p_vrs_fb, layers > 1);
}
}
}
diff --git a/servers/rendering/renderer_rd/effects/vrs.h b/servers/rendering/renderer_rd/effects/vrs.h
index 0f2bdd31b6..7125c6455d 100644
--- a/servers/rendering/renderer_rd/effects/vrs.h
+++ b/servers/rendering/renderer_rd/effects/vrs.h
@@ -66,10 +66,10 @@ public:
void copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multiview = false);
- void create_vrs_texture(const int p_base_width, const int p_base_height, const uint32_t p_view_count, RID &p_vrs_texture, RID &p_vrs_fb);
+ Size2i get_vrs_texture_size(const Size2i p_base_size) const;
void update_vrs_texture(RID p_vrs_fb, RID p_render_target);
};
} // namespace RendererRD
-#endif // !VRS_RD_H
+#endif // VRS_RD_H