initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import methods
Import("env")
env_effects = env.Clone()
# Thirdparty source files
thirdparty_obj = []
thirdparty_dir = "#thirdparty/amd-fsr2/"
thirdparty_sources = ["ffx_assert.cpp", "ffx_fsr2.cpp"]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_effects.Prepend(CPPEXTPATH=[thirdparty_dir])
def areatex_builder(target, source, env):
buffer = methods.get_buffer(str(source[0]))
with methods.generated_wrapper(str(target[0])) as file:
file.write(f"""\
#define AREATEX_WIDTH 160
#define AREATEX_HEIGHT 560
#define AREATEX_PITCH (AREATEX_WIDTH * 2)
#define AREATEX_SIZE (AREATEX_HEIGHT * AREATEX_PITCH)
inline constexpr const unsigned char area_tex_png[] = {{
{methods.format_buffer(buffer, 1)}
}};
""")
env.CommandNoCache("smaa_area_tex.gen.h", "#thirdparty/smaa/AreaTex.png", env.Run(areatex_builder))
def searchtex_builder(target, source, env):
buffer = methods.get_buffer(str(source[0]))
with methods.generated_wrapper(str(target[0])) as file:
file.write(f"""\
#define SEARCHTEX_WIDTH 64
#define SEARCHTEX_HEIGHT 16
#define SEARCHTEX_PITCH SEARCHTEX_WIDTH
#define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH)
inline constexpr const unsigned char search_tex_png[] = {{
{methods.format_buffer(buffer, 1)}
}};
""")
env.CommandNoCache("smaa_search_tex.gen.h", "#thirdparty/smaa/SearchTex.png", env.Run(searchtex_builder))
# This flag doesn't actually control anything GCC specific in FSR2. It determines
# if symbols should be exported, which is not required for Godot.
env_effects.Append(CPPDEFINES=["FFX_GCC"])
env_thirdparty = env_effects.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.servers_sources += thirdparty_obj
# Godot source files
module_obj = []
env_effects.add_source_files(module_obj, "*.cpp")
if env["metal"]:
env_effects.add_source_files(module_obj, "metal_fx.mm")
env.servers_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View File

@@ -0,0 +1,497 @@
/**************************************************************************/
/* bokeh_dof.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "bokeh_dof.h"
#include "copy_effects.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
#include "servers/rendering/storage/camera_attributes_storage.h"
using namespace RendererRD;
BokehDOF::BokehDOF(bool p_prefer_raster_effects) {
prefer_raster_effects = p_prefer_raster_effects;
// Initialize bokeh
Vector<String> bokeh_modes;
bokeh_modes.push_back("\n#define MODE_GEN_BLUR_SIZE\n");
bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n#define OUTPUT_WEIGHT\n");
bokeh_modes.push_back("\n#define MODE_BOKEH_BOX\n");
bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n#define OUTPUT_WEIGHT\n");
bokeh_modes.push_back("\n#define MODE_BOKEH_HEXAGONAL\n");
bokeh_modes.push_back("\n#define MODE_BOKEH_CIRCULAR\n#define OUTPUT_WEIGHT\n");
bokeh_modes.push_back("\n#define MODE_COMPOSITE_BOKEH\n");
if (prefer_raster_effects) {
bokeh.raster_shader.initialize(bokeh_modes);
bokeh.shader_version = bokeh.raster_shader.version_create();
const int att_count[BOKEH_MAX] = { 1, 2, 1, 2, 1, 2, 1 };
for (int i = 0; i < BOKEH_MAX; i++) {
RD::PipelineColorBlendState blend_state = (i == BOKEH_COMPOSITE) ? RD::PipelineColorBlendState::create_blend(att_count[i]) : RD::PipelineColorBlendState::create_disabled(att_count[i]);
bokeh.raster_pipelines[i].setup(bokeh.raster_shader.version_get_shader(bokeh.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
}
} else {
bokeh.compute_shader.initialize(bokeh_modes);
bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_BOX_NOWEIGHT, false);
bokeh.compute_shader.set_variant_enabled(BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT, false);
bokeh.shader_version = bokeh.compute_shader.version_create();
for (int i = 0; i < BOKEH_MAX; i++) {
if (bokeh.compute_shader.is_variant_enabled(i)) {
bokeh.compute_pipelines[i] = RD::get_singleton()->compute_pipeline_create(bokeh.compute_shader.version_get_shader(bokeh.shader_version, i));
}
}
for (int i = 0; i < BOKEH_MAX; i++) {
bokeh.raster_pipelines[i].clear();
}
}
}
BokehDOF::~BokehDOF() {
if (prefer_raster_effects) {
bokeh.raster_shader.version_free(bokeh.shader_version);
} else {
bokeh.compute_shader.version_free(bokeh.shader_version);
}
}
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();
ERR_FAIL_NULL(uniform_set_cache);
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 = 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 = (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;
bokeh.push_constant.blur_scale = 0.5;
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
RD::Uniform u_base_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.base_texture);
RD::Uniform u_secondary_image(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.secondary_texture);
RD::Uniform u_half_image0(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[0]);
RD::Uniform u_half_image1(RD::UNIFORM_TYPE_IMAGE, 0, p_buffers.half_texture[1]);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
/* FIRST PASS */
// The alpha channel of the source color texture is filled with the expected circle size
// If used for DOF far, the size is positive, if used for near, its negative.
RID shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
ERR_FAIL_COND(shader.is_null());
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BLUR_SIZE]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth_texture), 1);
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
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 (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
//second pass
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());
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[mode]);
static const int quality_samples[4] = { 6, 12, 12, 24 };
bokeh.push_constant.steps = quality_samples[blur_quality];
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);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
bokeh.push_constant.half_size = true;
bokeh.push_constant.blur_size *= 0.5;
} else {
//medium and high quality use full size
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_secondary_image), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
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);
//third pass
bokeh.push_constant.second_pass = true;
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 {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_secondary_texture), 1);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
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 (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);
ERR_FAIL_COND(shader.is_null());
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture1), 1);
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
bokeh.push_constant.half_size = false;
bokeh.push_constant.second_pass = false;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1);
}
} else {
//circle
shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BOKEH_CIRCULAR);
ERR_FAIL_COND(shader.is_null());
//second pass
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_GEN_BOKEH_CIRCULAR]);
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[blur_quality];
//circle always runs in half size, otherwise too expensive
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_base_texture), 1);
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
bokeh.push_constant.half_size = true;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
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);
//circle is just one pass, then upscale
// upscale
shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE);
ERR_FAIL_COND(shader.is_null());
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, bokeh.compute_pipelines[BOKEH_COMPOSITE]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_base_image), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1);
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y;
bokeh.push_constant.half_size = false;
bokeh.push_constant.second_pass = false;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &bokeh.push_constant, sizeof(BokehPushConstant));
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_end();
}
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();
ERR_FAIL_NULL(uniform_set_cache);
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));
bokeh.push_constant.orthogonal = p_cam_orthogonal;
bokeh.push_constant.size[0] = p_buffers.base_texture_size.width;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.height;
bokeh.push_constant.z_far = p_cam_zfar;
bokeh.push_constant.z_near = p_cam_znear;
bokeh.push_constant.second_pass = false;
bokeh.push_constant.half_size = false;
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);
RD::Uniform u_base_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.base_texture }));
RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.depth_texture }));
RD::Uniform u_secondary_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.secondary_texture }));
RD::Uniform u_half_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[0] }));
RD::Uniform u_half_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.half_texture[1] }));
RD::Uniform u_weight_texture0(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[0] }));
RD::Uniform u_weight_texture1(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[1] }));
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 (dof_far || dof_near) {
if (dof_far) {
bokeh.push_constant.blur_far_active = true;
bokeh.push_constant.blur_far_begin = dof_far_begin;
bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size;
}
if (dof_near) {
bokeh.push_constant.blur_near_active = true;
bokeh.push_constant.blur_near_begin = dof_near_begin;
bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size;
}
{
// generate our depth data
RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, BOKEH_GEN_BLUR_SIZE);
ERR_FAIL_COND(shader.is_null());
RID framebuffer = p_buffers.base_weight_fb;
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) {
// double pass approach
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 (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;
bokeh.push_constant.half_size = true;
bokeh.push_constant.blur_size *= 0.5;
}
static const int quality_samples[4] = { 6, 12, 12, 24 };
bokeh.push_constant.blur_scale = 0.5;
bokeh.push_constant.steps = quality_samples[blur_quality];
RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb;
// Pass 1
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
// Pass 2
if (!bokeh.push_constant.half_size) {
// do not output weight, we're writing back into our base buffer
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());
}
bokeh.push_constant.second_pass = true;
framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[1] : p_buffers.base_fb;
RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture;
RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1;
draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
if (bokeh.push_constant.half_size) {
// Compose pass
mode = BOKEH_COMPOSITE;
shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
framebuffer = p_buffers.base_fb;
draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
} else {
// circular is a single pass approach
BokehMode mode = BOKEH_GEN_BOKEH_CIRCULAR;
RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
{
// circle always runs in half size, otherwise too expensive (though the code below does support making this optional)
bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1;
bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1;
bokeh.push_constant.half_size = true;
// bokeh.push_constant.blur_size *= 0.5;
}
static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 };
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;
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
if (bokeh.push_constant.half_size) {
// Compose
mode = BOKEH_COMPOSITE;
shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
framebuffer = p_buffers.base_fb;
draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
} else {
CopyEffects::get_singleton()->copy_raster(p_buffers.secondary_texture, p_buffers.base_fb);
}
}
}
}

View File

@@ -0,0 +1,119 @@
/**************************************************************************/
/* bokeh_dof.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl.gen.h"
namespace RendererRD {
class BokehDOF {
private:
bool prefer_raster_effects;
struct BokehPushConstant {
uint32_t size[2];
float z_far;
float z_near;
uint32_t orthogonal;
float blur_size;
float blur_scale;
uint32_t steps;
uint32_t blur_near_active;
float blur_near_begin;
float blur_near_end;
uint32_t blur_far_active;
float blur_far_begin;
float blur_far_end;
uint32_t second_pass;
uint32_t half_size;
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];
};
enum BokehMode {
BOKEH_GEN_BLUR_SIZE,
BOKEH_GEN_BOKEH_BOX,
BOKEH_GEN_BOKEH_BOX_NOWEIGHT,
BOKEH_GEN_BOKEH_HEXAGONAL,
BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT,
BOKEH_GEN_BOKEH_CIRCULAR,
BOKEH_COMPOSITE,
BOKEH_MAX
};
struct Bokeh {
BokehPushConstant push_constant;
BokehDofShaderRD compute_shader;
BokehDofRasterShaderRD raster_shader;
RID shader_version;
RID compute_pipelines[BOKEH_MAX];
PipelineCacheRD raster_pipelines[BOKEH_MAX];
} bokeh;
public:
struct BokehBuffers {
// bokeh buffers
// textures
Size2i base_texture_size;
RID base_texture;
RID depth_texture;
RID secondary_texture;
RID half_texture[2];
// raster only
RID base_fb;
RID secondary_fb; // with weights
RID half_fb[2]; // with weights
RID base_weight_fb;
RID weight_texture[4];
};
BokehDOF(bool p_prefer_raster_effects);
~BokehDOF();
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,359 @@
/**************************************************************************/
/* copy_effects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/copy.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/cubemap_filter.glsl.gen.h"
#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"
namespace RendererRD {
class CopyEffects {
private:
bool prefer_raster_effects;
// Blur raster shader
enum BlurRasterMode {
BLUR_MIPMAP,
BLUR_MODE_GAUSSIAN_BLUR,
BLUR_MODE_GAUSSIAN_GLOW,
BLUR_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
BLUR_MODE_COPY,
BLUR_MODE_SET_COLOR,
BLUR_MODE_MAX
};
enum {
BLUR_FLAG_HORIZONTAL = (1 << 0),
BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 1),
BLUR_FLAG_GLOW_FIRST_PASS = (1 << 2),
};
struct BlurRasterPushConstant {
float pixel_size[2];
uint32_t flags;
uint32_t pad;
//glow
float glow_strength;
float glow_bloom;
float glow_hdr_threshold;
float glow_hdr_scale;
float glow_exposure;
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_scale;
float luminance_multiplier;
float res1;
float res2;
float res3;
};
struct BlurRaster {
BlurRasterPushConstant push_constant;
BlurRasterShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[BLUR_MODE_MAX];
} blur_raster;
// Copy shader
enum CopyMode {
COPY_MODE_GAUSSIAN_COPY,
COPY_MODE_GAUSSIAN_COPY_8BIT,
COPY_MODE_GAUSSIAN_GLOW,
COPY_MODE_GAUSSIAN_GLOW_AUTO_EXPOSURE,
COPY_MODE_SIMPLY_COPY,
COPY_MODE_SIMPLY_COPY_8BIT,
COPY_MODE_SIMPLY_COPY_DEPTH,
COPY_MODE_SET_COLOR,
COPY_MODE_SET_COLOR_8BIT,
COPY_MODE_MIPMAP,
COPY_MODE_LINEARIZE_DEPTH,
COPY_MODE_CUBE_TO_PANORAMA,
COPY_MODE_CUBE_ARRAY_TO_PANORAMA,
COPY_MODE_MAX,
};
enum {
COPY_FLAG_HORIZONTAL = (1 << 0),
COPY_FLAG_USE_COPY_SECTION = (1 << 1),
COPY_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2),
COPY_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3),
COPY_FLAG_GLOW_FIRST_PASS = (1 << 4),
COPY_FLAG_FLIP_Y = (1 << 5),
COPY_FLAG_FORCE_LUMINANCE = (1 << 6),
COPY_FLAG_ALL_SOURCE = (1 << 7),
COPY_FLAG_ALPHA_TO_ONE = (1 << 8),
};
struct CopyPushConstant {
int32_t section[4];
int32_t target[2];
uint32_t flags;
uint32_t pad;
// Glow.
float glow_strength;
float glow_bloom;
float glow_hdr_threshold;
float glow_hdr_scale;
float glow_exposure;
float glow_white;
float glow_luminance_cap;
float glow_auto_exposure_scale;
// DOF.
float camera_z_far;
float camera_z_near;
uint32_t pad2[2];
//SET color
float set_color[4];
};
struct Copy {
CopyPushConstant push_constant;
CopyShaderRD shader;
RID shader_version;
RID pipelines[COPY_MODE_MAX];
} copy;
// Copy to FB shader
enum CopyToFBMode {
COPY_TO_FB_COPY,
COPY_TO_FB_COPY_PANORAMA_TO_DP,
COPY_TO_FB_COPY2,
COPY_TO_FB_SET_COLOR,
// These variants are disabled unless XR shaders are enabled.
// They should be listed last.
COPY_TO_FB_MULTIVIEW,
COPY_TO_FB_MULTIVIEW_WITH_DEPTH,
COPY_TO_FB_MAX,
};
enum CopyToFBFlags {
COPY_TO_FB_FLAG_FLIP_Y = (1 << 0),
COPY_TO_FB_FLAG_USE_SECTION = (1 << 1),
COPY_TO_FB_FLAG_FORCE_LUMINANCE = (1 << 2),
COPY_TO_FB_FLAG_ALPHA_TO_ZERO = (1 << 3),
COPY_TO_FB_FLAG_SRGB = (1 << 4),
COPY_TO_FB_FLAG_ALPHA_TO_ONE = (1 << 5),
COPY_TO_FB_FLAG_LINEAR = (1 << 6),
COPY_TO_FB_FLAG_NORMAL = (1 << 7),
COPY_TO_FB_FLAG_USE_SRC_SECTION = (1 << 8),
};
struct CopyToFbPushConstant {
float section[4];
float pixel_size[2];
float luminance_multiplier;
uint32_t flags;
float set_color[4];
};
struct CopyToFb {
CopyToFbPushConstant push_constant;
CopyToFbShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[COPY_TO_FB_MAX];
} copy_to_fb;
// Copy to DP
struct CopyToDPPushConstant {
float z_far;
float z_near;
float texel_size[2];
};
struct CopyToDP {
CubeToDpShaderRD shader;
RID shader_version;
PipelineCacheRD pipeline;
} cube_to_dp;
// Cubemap effects
struct CubemapDownsamplerPushConstant {
uint32_t face_size;
uint32_t face_id;
float pad[2];
};
struct CubemapDownsampler {
CubemapDownsamplerPushConstant push_constant;
CubemapDownsamplerShaderRD compute_shader;
CubemapDownsamplerRasterShaderRD raster_shader;
RID shader_version;
RID compute_pipeline;
PipelineCacheRD raster_pipeline;
} cubemap_downsampler;
enum CubemapFilterMode {
FILTER_MODE_HIGH_QUALITY,
FILTER_MODE_LOW_QUALITY,
FILTER_MODE_HIGH_QUALITY_ARRAY,
FILTER_MODE_LOW_QUALITY_ARRAY,
FILTER_MODE_MAX,
};
struct CubemapFilterRasterPushConstant {
uint32_t mip_level;
uint32_t face_id;
float pad[2];
};
struct CubemapFilter {
CubemapFilterShaderRD compute_shader;
CubemapFilterRasterShaderRD raster_shader;
RID shader_version;
RID compute_pipelines[FILTER_MODE_MAX];
PipelineCacheRD raster_pipelines[FILTER_MODE_MAX];
RID uniform_set;
RID image_uniform_set;
RID coefficient_buffer;
bool use_high_quality;
} filter;
struct CubemapRoughnessPushConstant {
uint32_t face_id;
uint32_t sample_count;
float roughness;
uint32_t use_direct_write;
float face_size;
float pad[3];
};
struct CubemapRoughness {
CubemapRoughnessPushConstant push_constant;
CubemapRoughnessShaderRD compute_shader;
CubemapRoughnessRasterShaderRD raster_shader;
RID shader_version;
RID compute_pipeline;
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:
static CopyEffects *get_singleton();
CopyEffects(bool p_prefer_raster_effects);
~CopyEffects();
bool get_prefer_raster_effects() { return prefer_raster_effects; }
void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false, bool alpha_to_one = false, bool p_linear = false, bool p_normal = false, const Rect2 &p_src_rect = Rect2());
void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
void copy_to_drawlist(RD::DrawListID p_draw_list, RD::FramebufferFormatID p_fb_format, RID p_source_rd_texture, bool p_linear = false);
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, const Size2i &p_size, bool p_8bit_dst = false);
void gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_region, const Size2i &p_size);
void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, 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_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_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);
void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region);
void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
void cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, uint32_t p_mip_level);
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

View File

@@ -0,0 +1,378 @@
/**************************************************************************/
/* debug_effects.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "debug_effects.h"
#include "servers/rendering/renderer_rd/storage_rd/light_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
using namespace RendererRD;
DebugEffects::DebugEffects() {
{
// Shadow Frustum debug shader
Vector<String> modes;
modes.push_back("");
shadow_frustum.shader.initialize(modes);
shadow_frustum.shader_version = shadow_frustum.shader.version_create();
RD::PipelineRasterizationState raster_state = RD::PipelineRasterizationState();
shadow_frustum.pipelines[SFP_TRANSPARENT].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
raster_state.wireframe = true;
shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
{
// Motion Vectors debug shader.
Vector<String> modes;
modes.push_back("");
motion_vectors.shader.initialize(modes);
motion_vectors.shader_version = motion_vectors.shader.version_create();
motion_vectors.pipeline.setup(motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
}
}
void DebugEffects::_create_frustum_arrays() {
if (frustum.vertex_buffer.is_null()) {
// Create vertex buffer, but don't put data in it yet
frustum.vertex_buffer = RD::get_singleton()->vertex_buffer_create(8 * sizeof(float) * 3, Vector<uint8_t>());
Vector<RD::VertexAttribute> attributes;
Vector<RID> buffers;
RD::VertexAttribute vd;
vd.location = 0;
vd.stride = sizeof(float) * 3;
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
attributes.push_back(vd);
buffers.push_back(frustum.vertex_buffer);
frustum.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
frustum.vertex_array = RD::get_singleton()->vertex_array_create(8, frustum.vertex_format, buffers);
}
if (frustum.index_buffer.is_null()) {
uint16_t indices[6 * 2 * 3] = {
// Far
0, 1, 2, // FLT, FLB, FRT
1, 3, 2, // FLB, FRB, FRT
// Near
4, 6, 5, // NLT, NRT, NLB
6, 7, 5, // NRT, NRB, NLB
// Left
0, 4, 1, // FLT, NLT, FLB
4, 5, 1, // NLT, NLB, FLB
// Right
6, 2, 7, // NRT, FRT, NRB
2, 3, 7, // FRT, FRB, NRB
// Top
0, 2, 4, // FLT, FRT, NLT
2, 6, 4, // FRT, NRT, NLT
// Bottom
5, 7, 1, // NLB, NRB, FLB,
7, 3, 1, // NRB, FRB, FLB
};
// Create our index_array
PackedByteArray data;
data.resize(6 * 2 * 3 * 2);
{
uint8_t *w = data.ptrw();
uint16_t *p16 = (uint16_t *)w;
for (int i = 0; i < 6 * 2 * 3; i++) {
*p16 = indices[i];
p16++;
}
}
frustum.index_buffer = RD::get_singleton()->index_buffer_create(6 * 2 * 3, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16, data);
frustum.index_array = RD::get_singleton()->index_array_create(frustum.index_buffer, 0, 6 * 2 * 3);
}
if (frustum.lines_buffer.is_null()) {
uint16_t indices[12 * 2] = {
0, 1, // FLT - FLB
1, 3, // FLB - FRB
3, 2, // FRB - FRT
2, 0, // FRT - FLT
4, 6, // NLT - NRT
6, 7, // NRT - NRB
7, 5, // NRB - NLB
5, 4, // NLB - NLT
0, 4, // FLT - NLT
1, 5, // FLB - NLB
2, 6, // FRT - NRT
3, 7, // FRB - NRB
};
// Create our lines_array
PackedByteArray data;
data.resize(12 * 2 * 2);
{
uint8_t *w = data.ptrw();
uint16_t *p16 = (uint16_t *)w;
for (int i = 0; i < 12 * 2; i++) {
*p16 = indices[i];
p16++;
}
}
frustum.lines_buffer = RD::get_singleton()->index_buffer_create(12 * 2, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16, data);
frustum.lines_array = RD::get_singleton()->index_array_create(frustum.lines_buffer, 0, 12 * 2);
}
}
DebugEffects::~DebugEffects() {
shadow_frustum.shader.version_free(shadow_frustum.shader_version);
// Destroy vertex buffer and array.
if (frustum.vertex_buffer.is_valid()) {
RD::get_singleton()->free(frustum.vertex_buffer); // Array gets freed as dependency.
}
// Destroy index buffer and array,
if (frustum.index_buffer.is_valid()) {
RD::get_singleton()->free(frustum.index_buffer); // Array gets freed as dependency.
}
// Destroy lines buffer and array.
if (frustum.lines_buffer.is_valid()) {
RD::get_singleton()->free(frustum.lines_buffer); // Array gets freed as dependency.
}
motion_vectors.shader.version_free(motion_vectors.shader_version);
}
void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) {
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
RID base = light_storage->light_instance_get_base_light(p_light);
ERR_FAIL_COND(light_storage->light_get_type(base) != RS::LIGHT_DIRECTIONAL);
// Make sure our buffers and arrays exist.
_create_frustum_arrays();
// Setup a points buffer for our view frustum.
PackedByteArray points;
points.resize(8 * sizeof(float) * 3);
// Get info about our splits.
RS::LightDirectionalShadowMode shadow_mode = light_storage->light_directional_get_shadow_mode(base);
bool overlap = light_storage->light_directional_get_blend_splits(base);
int splits = 1;
if (shadow_mode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
splits = 4;
} else if (shadow_mode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
splits = 2;
}
// Setup our camera info (this is mostly a duplicate of the logic found in RendererSceneCull::_light_instance_setup_directional_shadow).
bool is_orthogonal = p_cam_projection.is_orthogonal();
real_t aspect = p_cam_projection.get_aspect();
real_t fov = 0.0;
Vector2 vp_he;
if (is_orthogonal) {
vp_he = p_cam_projection.get_viewport_half_extents();
} else {
fov = p_cam_projection.get_fov(); //this is actually yfov, because set aspect tries to keep it
}
real_t min_distance = p_cam_projection.get_z_near();
real_t max_distance = p_cam_projection.get_z_far();
real_t shadow_max = RSG::light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE);
if (shadow_max > 0 && !is_orthogonal) {
max_distance = MIN(shadow_max, max_distance);
}
// Make sure we've not got bad info coming in.
max_distance = MAX(max_distance, min_distance + 0.001);
min_distance = MIN(min_distance, max_distance);
real_t range = max_distance - min_distance;
real_t distances[5];
distances[0] = min_distance;
for (int i = 0; i < splits; i++) {
distances[i + 1] = min_distance + RSG::light_storage->light_get_param(base, RS::LightParam(RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range;
};
distances[splits] = max_distance;
Color colors[4] = {
Color(1.0, 0.0, 0.0, 0.1),
Color(0.0, 1.0, 0.0, 0.1),
Color(0.0, 0.0, 1.0, 0.1),
Color(1.0, 1.0, 0.0, 0.1),
};
for (int split = 0; split < splits; split++) {
// Load frustum points into vertex buffer.
uint8_t *w = points.ptrw();
Vector3 *vw = (Vector3 *)w;
Projection projection;
if (is_orthogonal) {
projection.set_orthogonal(vp_he.y * 2.0, aspect, distances[(split == 0 || !overlap) ? split : split - 1], distances[split + 1], false);
} else {
projection.set_perspective(fov, aspect, distances[(split == 0 || !overlap) ? split : split - 1], distances[split + 1], true);
}
bool res = projection.get_endpoints(p_cam_transform, vw);
ERR_CONTINUE(!res);
RD::get_singleton()->buffer_update(frustum.vertex_buffer, 0, 8 * sizeof(float) * 3, w);
// Get our light projection info.
Projection light_projection = light_storage->light_instance_get_shadow_camera(p_light, split);
Transform3D light_transform = light_storage->light_instance_get_shadow_transform(p_light, split);
Rect2 atlas_rect_norm = light_storage->light_instance_get_directional_shadow_atlas_rect(p_light, split);
if (!is_orthogonal) {
light_transform.orthogonalize();
}
// Setup our push constant.
ShadowFrustumPushConstant push_constant;
MaterialStorage::store_camera(light_projection * Projection(light_transform.inverse()), push_constant.mvp);
push_constant.color[0] = colors[split].r;
push_constant.color[1] = colors[split].g;
push_constant.color[2] = colors[split].b;
push_constant.color[3] = colors[split].a;
// Adjust our rect to our atlas position.
Rect2 rect = p_rect;
rect.position.x += atlas_rect_norm.position.x * rect.size.x;
rect.position.y += atlas_rect_norm.position.y * rect.size.y;
rect.size.x *= atlas_rect_norm.size.x;
rect.size.y *= atlas_rect_norm.size.y;
// And draw our frustum.
RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, rect);
RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
pipeline = shadow_frustum.pipelines[SFP_WIREFRAME].get_render_pipeline(frustum.vertex_format, fb_format_id);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.lines_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
RD::get_singleton()->draw_list_end();
if (split < (splits - 1) && splits > 1) {
// Also draw it in the last split so we get a proper overview of the whole view frustum...
// Get our light projection info.
light_projection = light_storage->light_instance_get_shadow_camera(p_light, (splits - 1));
light_transform = light_storage->light_instance_get_shadow_transform(p_light, (splits - 1));
atlas_rect_norm = light_storage->light_instance_get_directional_shadow_atlas_rect(p_light, (splits - 1));
if (!is_orthogonal) {
light_transform.orthogonalize();
}
// Update our push constant.
MaterialStorage::store_camera(light_projection * Projection(light_transform.inverse()), push_constant.mvp);
push_constant.color[0] = colors[split].r;
push_constant.color[1] = colors[split].g;
push_constant.color[2] = colors[split].b;
push_constant.color[3] = colors[split].a;
// Adjust our rect to our atlas position.
rect = p_rect;
rect.position.x += atlas_rect_norm.position.x * rect.size.x;
rect.position.y += atlas_rect_norm.position.y * rect.size.y;
rect.size.x *= atlas_rect_norm.size.x;
rect.size.y *= atlas_rect_norm.size.y;
draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector<Color>(), 1.0f, 0, rect);
pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
RD::get_singleton()->draw_list_end();
}
}
}
void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_fb, const Projection &p_current_projection, const Transform3D &p_current_transform, const Projection &p_previous_projection, const Transform3D &p_previous_transform, Size2i p_resolution) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_velocity }));
RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_depth }));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass()));
Projection correction;
correction.set_depth_correction(true, true, false);
Projection reprojection = (correction * p_previous_projection) * p_previous_transform.affine_inverse() * p_current_transform * (correction * p_current_projection).inverse();
RendererRD::MaterialStorage::store_camera(reprojection, motion_vectors.push_constant.reprojection_matrix);
motion_vectors.push_constant.resolution[0] = p_resolution.width;
motion_vectors.push_constant.resolution[1] = p_resolution.height;
motion_vectors.push_constant.force_derive_from_depth = false;
RID shader = motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_velocity, u_source_depth), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &motion_vectors.push_constant, sizeof(MotionVectorsPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
#ifdef DRAW_DERIVATION_FROM_DEPTH_ON_TOP
motion_vectors.push_constant.force_derive_from_depth = true;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &motion_vectors.push_constant, sizeof(MotionVectorsPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
#endif
RD::get_singleton()->draw_list_end();
}

View File

@@ -0,0 +1,95 @@
/**************************************************************************/
/* debug_effects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h"
namespace RendererRD {
class DebugEffects {
private:
struct {
RD::VertexFormatID vertex_format;
RID vertex_buffer;
RID vertex_array;
RID index_buffer;
RID index_array;
RID lines_buffer;
RID lines_array;
} frustum;
struct ShadowFrustumPushConstant {
float mvp[16];
float color[4];
};
enum ShadowFrustumPipelines {
SFP_TRANSPARENT,
SFP_WIREFRAME,
SFP_MAX
};
struct {
ShadowFrustumShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[SFP_MAX];
} shadow_frustum;
struct MotionVectorsPushConstant {
float reprojection_matrix[16];
float resolution[2];
uint32_t force_derive_from_depth;
float pad;
};
struct {
MotionVectorsShaderRD shader;
RID shader_version;
PipelineCacheRD pipeline;
MotionVectorsPushConstant push_constant;
} motion_vectors;
void _create_frustum_arrays();
protected:
public:
DebugEffects();
~DebugEffects();
void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect);
void draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_fb, const Projection &p_current_projection, const Transform3D &p_current_transform, const Projection &p_previous_projection, const Transform3D &p_previous_transform, Size2i p_resolution);
};
} // namespace RendererRD

View File

@@ -0,0 +1,124 @@
/**************************************************************************/
/* fsr.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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;
fsr_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_NORMAL\n");
fsr_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n");
fsr_shader.initialize(fsr_upscale_modes);
FSRShaderVariant variant;
if (RD::get_singleton()->has_feature(RD::SUPPORTS_HALF_FLOAT)) {
variant = FSR_SHADER_VARIANT_NORMAL;
} else {
variant = FSR_SHADER_VARIANT_FALLBACK;
}
shader_version = fsr_shader.version_create();
pipeline = RD::get_singleton()->compute_pipeline_create(fsr_shader.version_get_shader(shader_version, variant));
}
FSR::~FSR() {
fsr_shader.version_free(shader_version);
}
void FSR::process(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();
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************/
/* fsr.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "spatial_upscaler.h"
#include "../storage_rd/render_scene_buffers_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl.gen.h"
namespace RendererRD {
class FSR : public SpatialUpscaler {
public:
FSR();
~FSR();
virtual const Span<char> get_label() const final { return "FSR 1.0 Upscale"; }
virtual void ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) final {}
virtual void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) final;
private:
enum FSRShaderVariant {
FSR_SHADER_VARIANT_NORMAL,
FSR_SHADER_VARIANT_FALLBACK,
};
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

View File

@@ -0,0 +1,880 @@
/**************************************************************************/
/* fsr2.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "fsr2.h"
#include "../storage_rd/material_storage.h"
#include "../uniform_set_cache_rd.h"
using namespace RendererRD;
#ifndef _MSC_VER
#include <cwchar>
#define wcscpy_s wcscpy
#endif
static RD::TextureType ffx_resource_type_to_rd_texture_type(FfxResourceType p_type) {
switch (p_type) {
case FFX_RESOURCE_TYPE_TEXTURE1D:
return RD::TEXTURE_TYPE_1D;
case FFX_RESOURCE_TYPE_TEXTURE2D:
return RD::TEXTURE_TYPE_2D;
case FFX_RESOURCE_TYPE_TEXTURE3D:
return RD::TEXTURE_TYPE_3D;
default:
return RD::TEXTURE_TYPE_MAX;
}
}
static FfxResourceType rd_texture_type_to_ffx_resource_type(RD::TextureType p_type) {
switch (p_type) {
case RD::TEXTURE_TYPE_1D:
return FFX_RESOURCE_TYPE_TEXTURE1D;
case RD::TEXTURE_TYPE_2D:
return FFX_RESOURCE_TYPE_TEXTURE2D;
case RD::TEXTURE_TYPE_3D:
return FFX_RESOURCE_TYPE_TEXTURE3D;
default:
return FFX_RESOURCE_TYPE_BUFFER;
}
}
static RD::DataFormat ffx_surface_format_to_rd_format(FfxSurfaceFormat p_format) {
switch (p_format) {
case FFX_SURFACE_FORMAT_R32G32B32A32_TYPELESS:
return RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
case FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT:
return RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
case FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT:
return RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
case FFX_SURFACE_FORMAT_R16G16B16A16_UNORM:
return RD::DATA_FORMAT_R16G16B16A16_UNORM;
case FFX_SURFACE_FORMAT_R32G32_FLOAT:
return RD::DATA_FORMAT_R32G32_SFLOAT;
case FFX_SURFACE_FORMAT_R32_UINT:
return RD::DATA_FORMAT_R32_UINT;
case FFX_SURFACE_FORMAT_R8G8B8A8_TYPELESS:
return RD::DATA_FORMAT_R8G8B8A8_UNORM;
case FFX_SURFACE_FORMAT_R8G8B8A8_UNORM:
return RD::DATA_FORMAT_R8G8B8A8_UNORM;
case FFX_SURFACE_FORMAT_R11G11B10_FLOAT:
return RD::DATA_FORMAT_B10G11R11_UFLOAT_PACK32;
case FFX_SURFACE_FORMAT_R16G16_FLOAT:
return RD::DATA_FORMAT_R16G16_SFLOAT;
case FFX_SURFACE_FORMAT_R16G16_UINT:
return RD::DATA_FORMAT_R16G16_UINT;
case FFX_SURFACE_FORMAT_R16_FLOAT:
return RD::DATA_FORMAT_R16_SFLOAT;
case FFX_SURFACE_FORMAT_R16_UINT:
return RD::DATA_FORMAT_R16_UINT;
case FFX_SURFACE_FORMAT_R16_UNORM:
return RD::DATA_FORMAT_R16_UNORM;
case FFX_SURFACE_FORMAT_R16_SNORM:
return RD::DATA_FORMAT_R16_SNORM;
case FFX_SURFACE_FORMAT_R8_UNORM:
return RD::DATA_FORMAT_R8_UNORM;
case FFX_SURFACE_FORMAT_R8_UINT:
return RD::DATA_FORMAT_R8_UINT;
case FFX_SURFACE_FORMAT_R8G8_UNORM:
return RD::DATA_FORMAT_R8G8_UNORM;
case FFX_SURFACE_FORMAT_R32_FLOAT:
return RD::DATA_FORMAT_R32_SFLOAT;
default:
return RD::DATA_FORMAT_MAX;
}
}
static FfxSurfaceFormat rd_format_to_ffx_surface_format(RD::DataFormat p_format) {
switch (p_format) {
case RD::DATA_FORMAT_R32G32B32A32_SFLOAT:
return FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT;
case RD::DATA_FORMAT_R16G16B16A16_SFLOAT:
return FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT;
case RD::DATA_FORMAT_R16G16B16A16_UNORM:
return FFX_SURFACE_FORMAT_R16G16B16A16_UNORM;
case RD::DATA_FORMAT_R32G32_SFLOAT:
return FFX_SURFACE_FORMAT_R32G32_FLOAT;
case RD::DATA_FORMAT_R32_UINT:
return FFX_SURFACE_FORMAT_R32_UINT;
case RD::DATA_FORMAT_R8G8B8A8_UNORM:
return FFX_SURFACE_FORMAT_R8G8B8A8_UNORM;
case RD::DATA_FORMAT_B10G11R11_UFLOAT_PACK32:
return FFX_SURFACE_FORMAT_R11G11B10_FLOAT;
case RD::DATA_FORMAT_R16G16_SFLOAT:
return FFX_SURFACE_FORMAT_R16G16_FLOAT;
case RD::DATA_FORMAT_R16G16_UINT:
return FFX_SURFACE_FORMAT_R16G16_UINT;
case RD::DATA_FORMAT_R16_SFLOAT:
return FFX_SURFACE_FORMAT_R16_FLOAT;
case RD::DATA_FORMAT_R16_UINT:
return FFX_SURFACE_FORMAT_R16_UINT;
case RD::DATA_FORMAT_R16_UNORM:
return FFX_SURFACE_FORMAT_R16_UNORM;
case RD::DATA_FORMAT_R16_SNORM:
return FFX_SURFACE_FORMAT_R16_SNORM;
case RD::DATA_FORMAT_R8_UNORM:
return FFX_SURFACE_FORMAT_R8_UNORM;
case RD::DATA_FORMAT_R8_UINT:
return FFX_SURFACE_FORMAT_R8_UINT;
case RD::DATA_FORMAT_R8G8_UNORM:
return FFX_SURFACE_FORMAT_R8G8_UNORM;
case RD::DATA_FORMAT_R32_SFLOAT:
return FFX_SURFACE_FORMAT_R32_FLOAT;
default:
return FFX_SURFACE_FORMAT_UNKNOWN;
}
}
static uint32_t ffx_usage_to_rd_usage_flags(uint32_t p_flags) {
uint32_t ret = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
if (p_flags & FFX_RESOURCE_USAGE_RENDERTARGET) {
ret |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (p_flags & FFX_RESOURCE_USAGE_UAV) {
ret |= RD::TEXTURE_USAGE_STORAGE_BIT;
ret |= RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
ret |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
}
return ret;
}
static FfxErrorCode create_backend_context_rd(FfxFsr2Interface *p_backend_interface, FfxDevice p_device) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
// Store pointer to the device common to all contexts.
scratch.device = p_device;
// Create a ring buffer of uniform buffers.
// FIXME: This could be optimized to be a single memory block if it was possible for RD to create views into a particular memory range of a UBO.
for (uint32_t i = 0; i < FSR2_UBO_RING_BUFFER_SIZE; i++) {
scratch.ubo_ring_buffer[i] = RD::get_singleton()->uniform_buffer_create(FFX_MAX_CONST_SIZE * sizeof(uint32_t));
ERR_FAIL_COND_V(scratch.ubo_ring_buffer[i].is_null(), FFX_ERROR_BACKEND_API_ERROR);
}
return FFX_OK;
}
static FfxErrorCode get_device_capabilities_rd(FfxFsr2Interface *p_backend_interface, FfxDeviceCapabilities *p_out_device_capabilities, FfxDevice p_device) {
FSR2Effect::Device &effect_device = *reinterpret_cast<FSR2Effect::Device *>(p_device);
*p_out_device_capabilities = effect_device.capabilities;
return FFX_OK;
}
static FfxErrorCode destroy_backend_context_rd(FfxFsr2Interface *p_backend_interface) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
for (uint32_t i = 0; i < FSR2_UBO_RING_BUFFER_SIZE; i++) {
RD::get_singleton()->free(scratch.ubo_ring_buffer[i]);
}
return FFX_OK;
}
static FfxErrorCode create_resource_rd(FfxFsr2Interface *p_backend_interface, const FfxCreateResourceDescription *p_create_resource_description, FfxResourceInternal *p_out_resource) {
// FSR2's base implementation won't issue a call to create a heap type that isn't just default on its own,
// so we can safely ignore it as RD does not expose this concept.
ERR_FAIL_COND_V(p_create_resource_description->heapType != FFX_HEAP_TYPE_DEFAULT, FFX_ERROR_INVALID_ARGUMENT);
RenderingDevice *rd = RD::get_singleton();
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
FfxResourceDescription res_desc = p_create_resource_description->resourceDescription;
// FSR2's base implementation never requests buffer creation.
ERR_FAIL_COND_V(res_desc.type != FFX_RESOURCE_TYPE_TEXTURE1D && res_desc.type != FFX_RESOURCE_TYPE_TEXTURE2D && res_desc.type != FFX_RESOURCE_TYPE_TEXTURE3D, FFX_ERROR_INVALID_ARGUMENT);
if (res_desc.mipCount == 0) {
// Mipmap count must be derived from the resource's dimensions.
res_desc.mipCount = uint32_t(1 + std::floor(std::log2(MAX(MAX(res_desc.width, res_desc.height), res_desc.depth))));
}
Vector<PackedByteArray> initial_data;
if (p_create_resource_description->initDataSize) {
PackedByteArray byte_array;
byte_array.resize(p_create_resource_description->initDataSize);
memcpy(byte_array.ptrw(), p_create_resource_description->initData, p_create_resource_description->initDataSize);
initial_data.push_back(byte_array);
}
RD::TextureFormat texture_format;
texture_format.texture_type = ffx_resource_type_to_rd_texture_type(res_desc.type);
texture_format.format = ffx_surface_format_to_rd_format(res_desc.format);
texture_format.usage_bits = ffx_usage_to_rd_usage_flags(p_create_resource_description->usage);
texture_format.width = res_desc.width;
texture_format.height = res_desc.height;
texture_format.depth = res_desc.depth;
texture_format.mipmaps = res_desc.mipCount;
texture_format.is_discardable = true;
RID texture = rd->texture_create(texture_format, RD::TextureView(), initial_data);
ERR_FAIL_COND_V(texture.is_null(), FFX_ERROR_BACKEND_API_ERROR);
rd->set_resource_name(texture, String(p_create_resource_description->name));
// Add the resource to the storage and use the internal index to reference it.
p_out_resource->internalIndex = scratch.resources.add(texture, false, p_create_resource_description->id, res_desc);
return FFX_OK;
}
static FfxErrorCode register_resource_rd(FfxFsr2Interface *p_backend_interface, const FfxResource *p_in_resource, FfxResourceInternal *p_out_resource) {
if (p_in_resource->resource == nullptr) {
// Null resource case.
p_out_resource->internalIndex = -1;
return FFX_OK;
}
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
const RID &rid = *reinterpret_cast<const RID *>(p_in_resource->resource);
ERR_FAIL_COND_V(rid.is_null(), FFX_ERROR_INVALID_ARGUMENT);
// Add the resource to the storage and use the internal index to reference it.
p_out_resource->internalIndex = scratch.resources.add(rid, true, FSR2Context::RESOURCE_ID_DYNAMIC, p_in_resource->description);
return FFX_OK;
}
static FfxErrorCode unregister_resources_rd(FfxFsr2Interface *p_backend_interface) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
LocalVector<uint32_t> dynamic_list_copy = scratch.resources.dynamic_list;
for (uint32_t i : dynamic_list_copy) {
scratch.resources.remove(i);
}
return FFX_OK;
}
static FfxResourceDescription get_resource_description_rd(FfxFsr2Interface *p_backend_interface, FfxResourceInternal p_resource) {
if (p_resource.internalIndex != -1) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
return scratch.resources.descriptions[p_resource.internalIndex];
} else {
return {};
}
}
static FfxErrorCode destroy_resource_rd(FfxFsr2Interface *p_backend_interface, FfxResourceInternal p_resource) {
if (p_resource.internalIndex != -1) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
if (scratch.resources.rids[p_resource.internalIndex].is_valid()) {
RD::get_singleton()->free(scratch.resources.rids[p_resource.internalIndex]);
scratch.resources.remove(p_resource.internalIndex);
}
}
return FFX_OK;
}
static FfxErrorCode create_pipeline_rd(FfxFsr2Interface *p_backend_interface, FfxFsr2Pass p_pass, const FfxPipelineDescription *p_pipeline_description, FfxPipelineState *p_out_pipeline) {
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
FSR2Effect::Device &device = *reinterpret_cast<FSR2Effect::Device *>(scratch.device);
FSR2Effect::Pass &effect_pass = device.passes[p_pass];
if (effect_pass.pipeline.pipeline_rid.is_null()) {
// Create pipeline for the device if it hasn't been created yet.
effect_pass.root_signature.shader_rid = effect_pass.shader->version_get_shader(effect_pass.shader_version, effect_pass.shader_variant);
ERR_FAIL_COND_V(effect_pass.root_signature.shader_rid.is_null(), FFX_ERROR_BACKEND_API_ERROR);
effect_pass.pipeline.pipeline_rid = RD::get_singleton()->compute_pipeline_create(effect_pass.root_signature.shader_rid);
ERR_FAIL_COND_V(effect_pass.pipeline.pipeline_rid.is_null(), FFX_ERROR_BACKEND_API_ERROR);
}
// While this is not their intended use, we use the pipeline and root signature pointers to store the
// RIDs to the pipeline and shader that RD needs for the compute pipeline.
p_out_pipeline->pipeline = reinterpret_cast<FfxPipeline>(&effect_pass.pipeline);
p_out_pipeline->rootSignature = reinterpret_cast<FfxRootSignature>(&effect_pass.root_signature);
p_out_pipeline->srvCount = effect_pass.sampled_bindings.size();
ERR_FAIL_COND_V(p_out_pipeline->srvCount > FFX_MAX_NUM_SRVS, FFX_ERROR_OUT_OF_RANGE);
memcpy(p_out_pipeline->srvResourceBindings, effect_pass.sampled_bindings.ptr(), sizeof(FfxResourceBinding) * p_out_pipeline->srvCount);
p_out_pipeline->uavCount = effect_pass.storage_bindings.size();
ERR_FAIL_COND_V(p_out_pipeline->uavCount > FFX_MAX_NUM_UAVS, FFX_ERROR_OUT_OF_RANGE);
memcpy(p_out_pipeline->uavResourceBindings, effect_pass.storage_bindings.ptr(), sizeof(FfxResourceBinding) * p_out_pipeline->uavCount);
p_out_pipeline->constCount = effect_pass.uniform_bindings.size();
ERR_FAIL_COND_V(p_out_pipeline->constCount > FFX_MAX_NUM_CONST_BUFFERS, FFX_ERROR_OUT_OF_RANGE);
memcpy(p_out_pipeline->cbResourceBindings, effect_pass.uniform_bindings.ptr(), sizeof(FfxResourceBinding) * p_out_pipeline->constCount);
bool low_resolution_mvs = (p_pipeline_description->contextFlags & FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS) == 0;
if (p_pass == FFX_FSR2_PASS_ACCUMULATE || p_pass == FFX_FSR2_PASS_ACCUMULATE_SHARPEN) {
// Change the binding for motion vectors in this particular pass if low resolution MVs are used.
if (low_resolution_mvs) {
FfxResourceBinding &binding = p_out_pipeline->srvResourceBindings[2];
wcscpy_s(binding.name, L"r_dilated_motion_vectors");
}
}
return FFX_OK;
}
static FfxErrorCode destroy_pipeline_rd(FfxFsr2Interface *p_backend_interface, FfxPipelineState *p_pipeline) {
// We don't want to destroy pipelines when the FSR2 API deems it necessary as it'll do so whenever the context is destroyed.
return FFX_OK;
}
static FfxErrorCode schedule_gpu_job_rd(FfxFsr2Interface *p_backend_interface, const FfxGpuJobDescription *p_job) {
ERR_FAIL_NULL_V(p_backend_interface, FFX_ERROR_INVALID_ARGUMENT);
ERR_FAIL_NULL_V(p_job, FFX_ERROR_INVALID_ARGUMENT);
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
scratch.gpu_jobs.push_back(*p_job);
return FFX_OK;
}
static FfxErrorCode execute_gpu_job_clear_float_rd(FSR2Context::Scratch &p_scratch, const FfxClearFloatJobDescription &p_job) {
RID resource = p_scratch.resources.rids[p_job.target.internalIndex];
FfxResourceDescription &desc = p_scratch.resources.descriptions[p_job.target.internalIndex];
ERR_FAIL_COND_V(desc.type == FFX_RESOURCE_TYPE_BUFFER, FFX_ERROR_INVALID_ARGUMENT);
Color color(p_job.color[0], p_job.color[1], p_job.color[2], p_job.color[3]);
RD::get_singleton()->texture_clear(resource, color, 0, desc.mipCount, 0, 1);
return FFX_OK;
}
static FfxErrorCode execute_gpu_job_copy_rd(FSR2Context::Scratch &p_scratch, const FfxCopyJobDescription &p_job) {
RID src = p_scratch.resources.rids[p_job.src.internalIndex];
RID dst = p_scratch.resources.rids[p_job.dst.internalIndex];
FfxResourceDescription &src_desc = p_scratch.resources.descriptions[p_job.src.internalIndex];
FfxResourceDescription &dst_desc = p_scratch.resources.descriptions[p_job.dst.internalIndex];
ERR_FAIL_COND_V(src_desc.type == FFX_RESOURCE_TYPE_BUFFER, FFX_ERROR_INVALID_ARGUMENT);
ERR_FAIL_COND_V(dst_desc.type == FFX_RESOURCE_TYPE_BUFFER, FFX_ERROR_INVALID_ARGUMENT);
for (uint32_t mip_level = 0; mip_level < src_desc.mipCount; mip_level++) {
RD::get_singleton()->texture_copy(src, dst, Vector3(0, 0, 0), Vector3(0, 0, 0), Vector3(src_desc.width, src_desc.height, src_desc.depth), mip_level, mip_level, 0, 0);
}
return FFX_OK;
}
static FfxErrorCode execute_gpu_job_compute_rd(FSR2Context::Scratch &p_scratch, const FfxComputeJobDescription &p_job) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL_V(uniform_set_cache, FFX_ERROR_BACKEND_API_ERROR);
FSR2Effect::RootSignature &root_signature = *reinterpret_cast<FSR2Effect::RootSignature *>(p_job.pipeline.rootSignature);
ERR_FAIL_COND_V(root_signature.shader_rid.is_null(), FFX_ERROR_INVALID_ARGUMENT);
FSR2Effect::Pipeline &backend_pipeline = *reinterpret_cast<FSR2Effect::Pipeline *>(p_job.pipeline.pipeline);
ERR_FAIL_COND_V(backend_pipeline.pipeline_rid.is_null(), FFX_ERROR_INVALID_ARGUMENT);
thread_local LocalVector<RD::Uniform> compute_uniforms;
compute_uniforms.clear();
for (uint32_t i = 0; i < p_job.pipeline.srvCount; i++) {
RID texture_rid = p_scratch.resources.rids[p_job.srvs[i].internalIndex];
RD::Uniform texture_uniform(RD::UNIFORM_TYPE_TEXTURE, p_job.pipeline.srvResourceBindings[i].slotIndex, texture_rid);
compute_uniforms.push_back(texture_uniform);
}
for (uint32_t i = 0; i < p_job.pipeline.uavCount; i++) {
RID image_rid = p_scratch.resources.rids[p_job.uavs[i].internalIndex];
RD::Uniform storage_uniform;
storage_uniform.uniform_type = RD::UNIFORM_TYPE_IMAGE;
storage_uniform.binding = p_job.pipeline.uavResourceBindings[i].slotIndex;
if (p_job.uavMip[i] > 0) {
LocalVector<RID> &mip_slice_rids = p_scratch.resources.mip_slice_rids[p_job.uavs[i].internalIndex];
if (mip_slice_rids.is_empty()) {
mip_slice_rids.resize(p_scratch.resources.descriptions[p_job.uavs[i].internalIndex].mipCount);
}
ERR_FAIL_COND_V(p_job.uavMip[i] >= mip_slice_rids.size(), FFX_ERROR_INVALID_ARGUMENT);
if (mip_slice_rids[p_job.uavMip[i]].is_null()) {
mip_slice_rids[p_job.uavMip[i]] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), image_rid, 0, p_job.uavMip[i]);
}
ERR_FAIL_COND_V(mip_slice_rids[p_job.uavMip[i]].is_null(), FFX_ERROR_BACKEND_API_ERROR);
storage_uniform.append_id(mip_slice_rids[p_job.uavMip[i]]);
} else {
storage_uniform.append_id(image_rid);
}
compute_uniforms.push_back(storage_uniform);
}
for (uint32_t i = 0; i < p_job.pipeline.constCount; i++) {
RID buffer_rid = p_scratch.ubo_ring_buffer[p_scratch.ubo_ring_buffer_index];
p_scratch.ubo_ring_buffer_index = (p_scratch.ubo_ring_buffer_index + 1) % FSR2_UBO_RING_BUFFER_SIZE;
RD::get_singleton()->buffer_update(buffer_rid, 0, p_job.cbs[i].uint32Size * sizeof(uint32_t), p_job.cbs[i].data);
RD::Uniform buffer_uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, p_job.pipeline.cbResourceBindings[i].slotIndex, buffer_rid);
compute_uniforms.push_back(buffer_uniform);
}
FSR2Effect::Device &device = *reinterpret_cast<FSR2Effect::Device *>(p_scratch.device);
RD::Uniform u_point_clamp_sampler(RD::UniformType::UNIFORM_TYPE_SAMPLER, 0, device.point_clamp_sampler);
RD::Uniform u_linear_clamp_sampler(RD::UniformType::UNIFORM_TYPE_SAMPLER, 1, device.linear_clamp_sampler);
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, backend_pipeline.pipeline_rid);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(root_signature.shader_rid, 0, u_point_clamp_sampler, u_linear_clamp_sampler), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache_vec(root_signature.shader_rid, 1, compute_uniforms), 1);
RD::get_singleton()->compute_list_dispatch(compute_list, p_job.dimensions[0], p_job.dimensions[1], p_job.dimensions[2]);
RD::get_singleton()->compute_list_end();
return FFX_OK;
}
static FfxErrorCode execute_gpu_jobs_rd(FfxFsr2Interface *p_backend_interface, FfxCommandList p_command_list) {
ERR_FAIL_NULL_V(p_backend_interface, FFX_ERROR_INVALID_ARGUMENT);
FSR2Context::Scratch &scratch = *reinterpret_cast<FSR2Context::Scratch *>(p_backend_interface->scratchBuffer);
FfxErrorCode error_code = FFX_OK;
for (const FfxGpuJobDescription &job : scratch.gpu_jobs) {
switch (job.jobType) {
case FFX_GPU_JOB_CLEAR_FLOAT: {
error_code = execute_gpu_job_clear_float_rd(scratch, job.clearJobDescriptor);
} break;
case FFX_GPU_JOB_COPY: {
error_code = execute_gpu_job_copy_rd(scratch, job.copyJobDescriptor);
} break;
case FFX_GPU_JOB_COMPUTE: {
error_code = execute_gpu_job_compute_rd(scratch, job.computeJobDescriptor);
} break;
default: {
error_code = FFX_ERROR_INVALID_ARGUMENT;
} break;
}
if (error_code != FFX_OK) {
scratch.gpu_jobs.clear();
return error_code;
}
}
scratch.gpu_jobs.clear();
return FFX_OK;
}
static FfxResource get_resource_rd(RID *p_rid, const wchar_t *p_name) {
FfxResource res = {};
if (p_rid->is_null()) {
return res;
}
wcscpy_s(res.name, p_name);
RD::TextureFormat texture_format = RD::get_singleton()->texture_get_format(*p_rid);
res.description.type = rd_texture_type_to_ffx_resource_type(texture_format.texture_type);
res.description.format = rd_format_to_ffx_surface_format(texture_format.format);
res.description.width = texture_format.width;
res.description.height = texture_format.height;
res.description.depth = texture_format.depth;
res.description.mipCount = texture_format.mipmaps;
res.description.flags = FFX_RESOURCE_FLAGS_NONE;
res.resource = reinterpret_cast<void *>(p_rid);
res.isDepth = texture_format.usage_bits & RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
return res;
}
FSR2Context::~FSR2Context() {
ffxFsr2ContextDestroy(&fsr_context);
}
FSR2Effect::FSR2Effect() {
FfxDeviceCapabilities &capabilities = device.capabilities;
capabilities.minimumSupportedShaderModel = FFX_SHADER_MODEL_5_1;
capabilities.waveLaneCountMin = 32;
capabilities.waveLaneCountMax = 32;
capabilities.fp16Supported = RD::get_singleton()->has_feature(RD::Features::SUPPORTS_HALF_FLOAT);
capabilities.raytracingSupported = false;
String general_defines =
"\n#define FFX_GPU\n"
"\n#define FFX_GLSL 1\n"
"\n#define FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS 1\n"
"\n#define FFX_FSR2_OPTION_HDR_COLOR_INPUT 1\n"
"\n#define FFX_FSR2_OPTION_INVERTED_DEPTH 1\n"
"\n#define FFX_FSR2_OPTION_GODOT_REACTIVE_MASK_CLAMP 1\n"
"\n#define FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS 1\n";
Vector<String> modes_single;
modes_single.push_back("");
Vector<String> modes_with_fp16;
modes_with_fp16.push_back("");
modes_with_fp16.push_back("\n#define FFX_HALF 1\n");
// Since Godot currently lacks a shader reflection mechanism to persist the name of the bindings in the shader cache and
// there's also no mechanism to compile the shaders offline, the bindings are created manually by looking at the GLSL
// files included in FSR2 and mapping the macro bindings (#define FSR2_BIND_*) to their respective implementation names.
//
// It is not guaranteed these will remain consistent at all between versions of FSR2, so it'll be necessary to keep these
// bindings up to date whenever the library is updated. In such cases, it is very likely the validation layer will throw an
// error if the bindings do not match.
{
Pass &pass = device.passes[FFX_FSR2_PASS_DEPTH_CLIP];
pass.shader = &shaders.depth_clip;
pass.shader->initialize(modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported ? 1 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_reconstructed_previous_nearest_depth" },
FfxResourceBinding{ 1, 0, L"r_dilated_motion_vectors" },
FfxResourceBinding{ 2, 0, L"r_dilatedDepth" },
FfxResourceBinding{ 3, 0, L"r_reactive_mask" },
FfxResourceBinding{ 4, 0, L"r_transparency_and_composition_mask" },
FfxResourceBinding{ 6, 0, L"r_previous_dilated_motion_vectors" },
FfxResourceBinding{ 7, 0, L"r_input_motion_vectors" },
FfxResourceBinding{ 8, 0, L"r_input_color_jittered" },
FfxResourceBinding{ 9, 0, L"r_input_depth" },
FfxResourceBinding{ 10, 0, L"r_input_exposure" }
};
pass.storage_bindings = {
// FSR2_BIND_UAV_DEPTH_CLIP (11) does not point to anything.
FfxResourceBinding{ 12, 0, L"rw_dilated_reactive_masks" },
FfxResourceBinding{ 13, 0, L"rw_prepared_input_color" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 14, 0, L"cbFSR2" }
};
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_RECONSTRUCT_PREVIOUS_DEPTH];
pass.shader = &shaders.reconstruct_previous_depth;
pass.shader->initialize(modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported ? 1 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_motion_vectors" },
FfxResourceBinding{ 1, 0, L"r_input_depth" },
FfxResourceBinding{ 2, 0, L"r_input_color_jittered" },
FfxResourceBinding{ 3, 0, L"r_input_exposure" },
FfxResourceBinding{ 4, 0, L"r_luma_history" }
};
pass.storage_bindings = {
FfxResourceBinding{ 5, 0, L"rw_reconstructed_previous_nearest_depth" },
FfxResourceBinding{ 6, 0, L"rw_dilated_motion_vectors" },
FfxResourceBinding{ 7, 0, L"rw_dilatedDepth" },
FfxResourceBinding{ 8, 0, L"rw_prepared_input_color" },
FfxResourceBinding{ 9, 0, L"rw_luma_history" },
// FSR2_BIND_UAV_LUMA_INSTABILITY (10) does not point to anything.
FfxResourceBinding{ 11, 0, L"rw_lock_input_luma" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 12, 0, L"cbFSR2" }
};
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_LOCK];
pass.shader = &shaders.lock;
pass.shader->initialize(modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported ? 1 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_lock_input_luma" }
};
pass.storage_bindings = {
FfxResourceBinding{ 1, 0, L"rw_new_locks" },
FfxResourceBinding{ 2, 0, L"rw_reconstructed_previous_nearest_depth" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 3, 0, L"cbFSR2" }
};
}
{
Vector<String> accumulate_modes_with_fp16;
accumulate_modes_with_fp16.push_back("\n");
accumulate_modes_with_fp16.push_back("\n#define FFX_FSR2_OPTION_APPLY_SHARPENING 1\n");
accumulate_modes_with_fp16.push_back("\n#define FFX_HALF 1\n");
accumulate_modes_with_fp16.push_back("\n#define FFX_HALF 1\n#define FFX_FSR2_OPTION_APPLY_SHARPENING 1\n");
// Workaround: Disable FP16 path for the accumulate pass on NVIDIA due to reduced occupancy and high VRAM throughput.
const bool fp16_path_supported = RD::get_singleton()->get_device_vendor_name() != "NVIDIA";
Pass &pass = device.passes[FFX_FSR2_PASS_ACCUMULATE];
pass.shader = &shaders.accumulate;
pass.shader->initialize(accumulate_modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported && fp16_path_supported ? 2 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_exposure" },
FfxResourceBinding{ 1, 0, L"r_dilated_reactive_masks" },
FfxResourceBinding{ 2, 0, L"r_input_motion_vectors" },
FfxResourceBinding{ 3, 0, L"r_internal_upscaled_color" },
FfxResourceBinding{ 4, 0, L"r_lock_status" },
FfxResourceBinding{ 5, 0, L"r_input_depth" },
FfxResourceBinding{ 6, 0, L"r_prepared_input_color" },
// FSR2_BIND_SRV_LUMA_INSTABILITY(7) does not point to anything.
FfxResourceBinding{ 8, 0, L"r_lanczos_lut" },
FfxResourceBinding{ 9, 0, L"r_upsample_maximum_bias_lut" },
FfxResourceBinding{ 10, 0, L"r_imgMips" },
FfxResourceBinding{ 11, 0, L"r_auto_exposure" },
FfxResourceBinding{ 12, 0, L"r_luma_history" }
};
pass.storage_bindings = {
FfxResourceBinding{ 13, 0, L"rw_internal_upscaled_color" },
FfxResourceBinding{ 14, 0, L"rw_lock_status" },
FfxResourceBinding{ 15, 0, L"rw_upscaled_output" },
FfxResourceBinding{ 16, 0, L"rw_new_locks" },
FfxResourceBinding{ 17, 0, L"rw_luma_history" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 18, 0, L"cbFSR2" }
};
// Sharpen pass is a clone of the accumulate pass with the sharpening variant.
Pass &sharpen_pass = device.passes[FFX_FSR2_PASS_ACCUMULATE_SHARPEN];
sharpen_pass = pass;
sharpen_pass.shader_variant = pass.shader_variant + 1;
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_RCAS];
pass.shader = &shaders.rcas;
pass.shader->initialize(modes_single, general_defines);
pass.shader_version = pass.shader->version_create();
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_exposure" },
FfxResourceBinding{ 1, 0, L"r_rcas_input" }
};
pass.storage_bindings = {
FfxResourceBinding{ 2, 0, L"rw_upscaled_output" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 3, 0, L"cbFSR2" },
FfxResourceBinding{ 4, 0, L"cbRCAS" }
};
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_COMPUTE_LUMINANCE_PYRAMID];
pass.shader = &shaders.compute_luminance_pyramid;
pass.shader->initialize(modes_single, general_defines);
pass.shader_version = pass.shader->version_create();
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_color_jittered" }
};
pass.storage_bindings = {
FfxResourceBinding{ 1, 0, L"rw_spd_global_atomic" },
FfxResourceBinding{ 2, 0, L"rw_img_mip_shading_change" },
FfxResourceBinding{ 3, 0, L"rw_img_mip_5" },
FfxResourceBinding{ 4, 0, L"rw_auto_exposure" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 5, 0, L"cbFSR2" },
FfxResourceBinding{ 6, 0, L"cbSPD" }
};
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_GENERATE_REACTIVE];
pass.shader = &shaders.autogen_reactive;
pass.shader->initialize(modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported ? 1 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_opaque_only" },
FfxResourceBinding{ 1, 0, L"r_input_color_jittered" }
};
pass.storage_bindings = {
FfxResourceBinding{ 2, 0, L"rw_output_autoreactive" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 3, 0, L"cbGenerateReactive" },
FfxResourceBinding{ 4, 0, L"cbFSR2" }
};
}
{
Pass &pass = device.passes[FFX_FSR2_PASS_TCR_AUTOGENERATE];
pass.shader = &shaders.tcr_autogen;
pass.shader->initialize(modes_with_fp16, general_defines);
pass.shader_version = pass.shader->version_create();
pass.shader_variant = capabilities.fp16Supported ? 1 : 0;
pass.sampled_bindings = {
FfxResourceBinding{ 0, 0, L"r_input_opaque_only" },
FfxResourceBinding{ 1, 0, L"r_input_color_jittered" },
FfxResourceBinding{ 2, 0, L"r_input_motion_vectors" },
FfxResourceBinding{ 3, 0, L"r_input_prev_color_pre_alpha" },
FfxResourceBinding{ 4, 0, L"r_input_prev_color_post_alpha" },
FfxResourceBinding{ 5, 0, L"r_reactive_mask" },
FfxResourceBinding{ 6, 0, L"r_transparency_and_composition_mask" },
FfxResourceBinding{ 13, 0, L"r_input_depth" }
};
pass.storage_bindings = {
FfxResourceBinding{ 7, 0, L"rw_output_autoreactive" },
FfxResourceBinding{ 8, 0, L"rw_output_autocomposition" },
FfxResourceBinding{ 9, 0, L"rw_output_prev_color_pre_alpha" },
FfxResourceBinding{ 10, 0, L"rw_output_prev_color_post_alpha" }
};
pass.uniform_bindings = {
FfxResourceBinding{ 11, 0, L"cbFSR2" },
FfxResourceBinding{ 12, 0, L"cbGenerateReactive" }
};
}
RD::SamplerState state;
state.mag_filter = RD::SAMPLER_FILTER_NEAREST;
state.min_filter = RD::SAMPLER_FILTER_NEAREST;
state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE;
state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE;
state.repeat_w = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE;
state.min_lod = -1000.0f;
state.max_lod = 1000.0f;
state.anisotropy_max = 1.0;
device.point_clamp_sampler = RD::get_singleton()->sampler_create(state);
ERR_FAIL_COND(device.point_clamp_sampler.is_null());
state.mag_filter = RD::SAMPLER_FILTER_LINEAR;
state.min_filter = RD::SAMPLER_FILTER_LINEAR;
device.linear_clamp_sampler = RD::get_singleton()->sampler_create(state);
ERR_FAIL_COND(device.linear_clamp_sampler.is_null());
}
FSR2Effect::~FSR2Effect() {
RD::get_singleton()->free(device.point_clamp_sampler);
RD::get_singleton()->free(device.linear_clamp_sampler);
for (uint32_t i = 0; i < FFX_FSR2_PASS_COUNT; i++) {
device.passes[i].shader->version_free(device.passes[i].shader_version);
}
}
FSR2Context *FSR2Effect::create_context(Size2i p_internal_size, Size2i p_target_size) {
FSR2Context *context = memnew(RendererRD::FSR2Context);
context->fsr_desc.flags = FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE | FFX_FSR2_ENABLE_DEPTH_INVERTED;
context->fsr_desc.maxRenderSize.width = p_internal_size.x;
context->fsr_desc.maxRenderSize.height = p_internal_size.y;
context->fsr_desc.displaySize.width = p_target_size.x;
context->fsr_desc.displaySize.height = p_target_size.y;
context->fsr_desc.device = &device;
FfxFsr2Interface &functions = context->fsr_desc.callbacks;
functions.fpCreateBackendContext = create_backend_context_rd;
functions.fpGetDeviceCapabilities = get_device_capabilities_rd;
functions.fpDestroyBackendContext = destroy_backend_context_rd;
functions.fpCreateResource = create_resource_rd;
functions.fpRegisterResource = register_resource_rd;
functions.fpUnregisterResources = unregister_resources_rd;
functions.fpGetResourceDescription = get_resource_description_rd;
functions.fpDestroyResource = destroy_resource_rd;
functions.fpCreatePipeline = create_pipeline_rd;
functions.fpDestroyPipeline = destroy_pipeline_rd;
functions.fpScheduleGpuJob = schedule_gpu_job_rd;
functions.fpExecuteGpuJobs = execute_gpu_jobs_rd;
functions.scratchBuffer = &context->scratch;
functions.scratchBufferSize = sizeof(context->scratch);
FfxErrorCode result = ffxFsr2ContextCreate(&context->fsr_context, &context->fsr_desc);
if (result == FFX_OK) {
return context;
} else {
memdelete(context);
return nullptr;
}
}
void FSR2Effect::upscale(const Parameters &p_params) {
// TODO: Transparency & Composition mask is not implemented.
FfxFsr2DispatchDescription dispatch_desc = {};
RID color = p_params.color;
RID depth = p_params.depth;
RID velocity = p_params.velocity;
RID reactive = p_params.reactive;
RID exposure = p_params.exposure;
RID output = p_params.output;
dispatch_desc.commandList = nullptr;
dispatch_desc.color = get_resource_rd(&color, L"color");
dispatch_desc.depth = get_resource_rd(&depth, L"depth");
dispatch_desc.motionVectors = get_resource_rd(&velocity, L"velocity");
dispatch_desc.reactive = get_resource_rd(&reactive, L"reactive");
dispatch_desc.exposure = get_resource_rd(&exposure, L"exposure");
dispatch_desc.transparencyAndComposition = {};
dispatch_desc.output = get_resource_rd(&output, L"output");
dispatch_desc.colorOpaqueOnly = {};
dispatch_desc.jitterOffset.x = p_params.jitter.x;
dispatch_desc.jitterOffset.y = p_params.jitter.y;
dispatch_desc.motionVectorScale.x = float(p_params.internal_size.width);
dispatch_desc.motionVectorScale.y = float(p_params.internal_size.height);
dispatch_desc.reset = p_params.reset_accumulation;
dispatch_desc.renderSize.width = p_params.internal_size.width;
dispatch_desc.renderSize.height = p_params.internal_size.height;
dispatch_desc.enableSharpening = (p_params.sharpness > 1e-6f);
dispatch_desc.sharpness = p_params.sharpness;
dispatch_desc.frameTimeDelta = p_params.delta_time;
dispatch_desc.preExposure = 1.0f;
dispatch_desc.cameraNear = p_params.z_near;
dispatch_desc.cameraFar = p_params.z_far;
dispatch_desc.cameraFovAngleVertical = p_params.fovy;
dispatch_desc.viewSpaceToMetersFactor = 1.0f;
dispatch_desc.enableAutoReactive = false;
dispatch_desc.autoTcThreshold = 1.0f;
dispatch_desc.autoTcScale = 1.0f;
dispatch_desc.autoReactiveScale = 1.0f;
dispatch_desc.autoReactiveMax = 1.0f;
RendererRD::MaterialStorage::store_camera(p_params.reprojection, dispatch_desc.reprojectionMatrix);
FfxErrorCode result = ffxFsr2ContextDispatch(&p_params.context->fsr_context, &dispatch_desc);
ERR_FAIL_COND(result != FFX_OK);
}

View File

@@ -0,0 +1,196 @@
/**************************************************************************/
/* fsr2.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_accumulate_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_autogen_reactive_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_compute_luminance_pyramid_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_depth_clip_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_lock_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_rcas_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_reconstruct_previous_depth_pass.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/fsr2/fsr2_tcr_autogen_pass.glsl.gen.h"
// This flag doesn't actually control anything GCC specific in FSR2. It determines
// if symbols should be exported, which is not required for Godot.
#ifndef FFX_GCC
#define FFX_GCC
#endif
#include "thirdparty/amd-fsr2/ffx_fsr2.h"
#define FSR2_MAX_QUEUED_FRAMES (4)
#define FSR2_MAX_UNIFORM_BUFFERS (4)
#define FSR2_MAX_BUFFERED_DESCRIPTORS (FFX_FSR2_PASS_COUNT * FSR2_MAX_QUEUED_FRAMES)
#define FSR2_UBO_RING_BUFFER_SIZE (FSR2_MAX_BUFFERED_DESCRIPTORS * FSR2_MAX_UNIFORM_BUFFERS)
namespace RendererRD {
class FSR2Context {
public:
enum ResourceID : uint32_t {
RESOURCE_ID_DYNAMIC = 0xFFFFFFFF
};
struct Resources {
LocalVector<RID> rids;
LocalVector<LocalVector<RID>> mip_slice_rids;
LocalVector<uint32_t> ids;
LocalVector<FfxResourceDescription> descriptions;
LocalVector<uint32_t> dynamic_list;
LocalVector<uint32_t> free_list;
uint32_t add(RID p_rid, bool p_dynamic, uint32_t p_id, FfxResourceDescription p_description) {
uint32_t ret_index;
if (free_list.is_empty()) {
ret_index = rids.size();
uint32_t new_size = ret_index + 1;
rids.resize(new_size);
mip_slice_rids.resize(new_size);
ids.resize(new_size);
descriptions.resize(new_size);
} else {
uint32_t end_index = free_list.size() - 1;
ret_index = free_list[end_index];
free_list.resize(end_index);
}
rids[ret_index] = p_rid;
mip_slice_rids[ret_index].clear();
ids[ret_index] = p_id;
descriptions[ret_index] = p_description;
if (p_dynamic) {
dynamic_list.push_back(ret_index);
}
return ret_index;
}
void remove(uint32_t p_index) {
DEV_ASSERT(p_index < rids.size());
free_list.push_back(p_index);
rids[p_index] = RID();
mip_slice_rids[p_index].clear();
ids[p_index] = 0;
descriptions[p_index] = {};
dynamic_list.erase(p_index);
}
uint32_t size() const {
return rids.size();
}
};
struct Scratch {
Resources resources;
LocalVector<FfxGpuJobDescription> gpu_jobs;
RID ubo_ring_buffer[FSR2_UBO_RING_BUFFER_SIZE];
uint32_t ubo_ring_buffer_index = 0;
FfxDevice device = nullptr;
};
Scratch scratch;
FfxFsr2Context fsr_context;
FfxFsr2ContextDescription fsr_desc;
~FSR2Context();
};
class FSR2Effect {
public:
struct RootSignature {
// Proxy structure to store the shader required by RD that uses the terminology used by the FSR2 API.
RID shader_rid;
};
struct Pipeline {
RID pipeline_rid;
};
struct Pass {
ShaderRD *shader;
RID shader_version;
RootSignature root_signature;
uint32_t shader_variant = 0;
Pipeline pipeline;
Vector<FfxResourceBinding> sampled_bindings;
Vector<FfxResourceBinding> storage_bindings;
Vector<FfxResourceBinding> uniform_bindings;
};
struct Device {
Pass passes[FFX_FSR2_PASS_COUNT];
FfxDeviceCapabilities capabilities;
RID point_clamp_sampler;
RID linear_clamp_sampler;
};
struct Parameters {
FSR2Context *context;
Size2i internal_size;
RID color;
RID depth;
RID velocity;
RID reactive;
RID exposure;
RID output;
float z_near = 0.0f;
float z_far = 0.0f;
float fovy = 0.0f;
Vector2 jitter;
float delta_time = 0.0f;
float sharpness = 0.0f;
bool reset_accumulation = false;
Projection reprojection;
};
FSR2Effect();
~FSR2Effect();
FSR2Context *create_context(Size2i p_internal_size, Size2i p_target_size);
void upscale(const Parameters &p_params);
private:
struct {
Fsr2DepthClipPassShaderRD depth_clip;
Fsr2ReconstructPreviousDepthPassShaderRD reconstruct_previous_depth;
Fsr2LockPassShaderRD lock;
Fsr2AccumulatePassShaderRD accumulate;
Fsr2AccumulatePassShaderRD accumulate_sharpen;
Fsr2RcasPassShaderRD rcas;
Fsr2ComputeLuminancePyramidPassShaderRD compute_luminance_pyramid;
Fsr2AutogenReactivePassShaderRD autogen_reactive;
Fsr2TcrAutogenPassShaderRD tcr_autogen;
} shaders;
Device device;
};
} // namespace RendererRD

View File

@@ -0,0 +1,256 @@
/**************************************************************************/
/* luminance.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "luminance.h"
#include "../framebuffer_cache_rd.h"
#include "../uniform_set_cache_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
using namespace RendererRD;
Luminance::Luminance(bool p_prefer_raster_effects) {
prefer_raster_effects = p_prefer_raster_effects;
if (prefer_raster_effects) {
Vector<String> luminance_reduce_modes;
luminance_reduce_modes.push_back("\n#define FIRST_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FIRST
luminance_reduce_modes.push_back("\n"); // LUMINANCE_REDUCE_FRAGMENT
luminance_reduce_modes.push_back("\n#define FINAL_PASS\n"); // LUMINANCE_REDUCE_FRAGMENT_FINAL
luminance_reduce_raster.shader.initialize(luminance_reduce_modes);
luminance_reduce_raster.shader_version = luminance_reduce_raster.shader.version_create();
for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
luminance_reduce_raster.pipelines[i].setup(luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
} else {
// Initialize luminance_reduce
Vector<String> luminance_reduce_modes;
luminance_reduce_modes.push_back("\n#define READ_TEXTURE\n");
luminance_reduce_modes.push_back("\n");
luminance_reduce_modes.push_back("\n#define WRITE_LUMINANCE\n");
luminance_reduce.shader.initialize(luminance_reduce_modes);
luminance_reduce.shader_version = luminance_reduce.shader.version_create();
for (int i = 0; i < LUMINANCE_REDUCE_MAX; i++) {
luminance_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, i));
}
for (int i = 0; i < LUMINANCE_REDUCE_FRAGMENT_MAX; i++) {
luminance_reduce_raster.pipelines[i].clear();
}
}
}
Luminance::~Luminance() {
if (prefer_raster_effects) {
luminance_reduce_raster.shader.version_free(luminance_reduce_raster.shader_version);
} else {
luminance_reduce.shader.version_free(luminance_reduce.shader_version);
}
}
void Luminance::LuminanceBuffers::set_prefer_raster_effects(bool p_prefer_raster_effects) {
prefer_raster_effects = p_prefer_raster_effects;
}
void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffers) {
Size2i internal_size = p_render_buffers->get_internal_size();
int w = internal_size.x;
int h = internal_size.y;
while (true) {
w = MAX(w / 8, 1);
h = MAX(h / 8, 1);
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R32_SFLOAT;
tf.width = w;
tf.height = h;
bool final = w == 1 && h == 1;
if (prefer_raster_effects) {
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
} else {
tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
}
if (final) {
tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
}
RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
reduce.push_back(texture);
if (final) {
current = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::get_singleton()->texture_clear(current, Color(0.0, 0.0, 0.0), 0u, 1u, 0u, 1u);
break;
}
}
}
void Luminance::LuminanceBuffers::free_data() {
for (int i = 0; i < reduce.size(); i++) {
RD::get_singleton()->free(reduce[i]);
}
reduce.clear();
if (current.is_valid()) {
RD::get_singleton()->free(current);
current = RID();
}
}
Ref<Luminance::LuminanceBuffers> Luminance::get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers) {
if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
return p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
}
Ref<LuminanceBuffers> buffers;
buffers.instantiate();
buffers->set_prefer_raster_effects(prefer_raster_effects);
buffers->configure(p_render_buffers.ptr());
p_render_buffers->set_custom_data(RB_LUMINANCE_BUFFERS, buffers);
return buffers;
}
RID Luminance::get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers) {
if (p_render_buffers->has_custom_data(RB_LUMINANCE_BUFFERS)) {
Ref<LuminanceBuffers> buffers = p_render_buffers->get_custom_data(RB_LUMINANCE_BUFFERS);
return buffers->current;
}
return RID();
}
void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
if (prefer_raster_effects) {
LuminanceReduceRasterPushConstant push_constant;
memset(&push_constant, 0, sizeof(LuminanceReduceRasterPushConstant));
push_constant.max_luminance = p_max_luminance;
push_constant.min_luminance = p_min_luminance;
push_constant.exposure_adjust = p_adjust;
for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
push_constant.source_size[0] = i == 0 ? p_source_size.x : push_constant.dest_size[0];
push_constant.source_size[1] = i == 0 ? p_source_size.y : push_constant.dest_size[1];
push_constant.dest_size[0] = MAX(push_constant.source_size[0] / 8, 1);
push_constant.dest_size[1] = MAX(push_constant.source_size[1] / 8, 1);
bool final = !p_set && (push_constant.dest_size[0] == 1) && (push_constant.dest_size[1] == 1);
LuminanceReduceRasterMode mode = final ? LUMINANCE_REDUCE_FRAGMENT_FINAL : (i == 0 ? LUMINANCE_REDUCE_FRAGMENT_FIRST : LUMINANCE_REDUCE_FRAGMENT);
RID shader = luminance_reduce_raster.shader.version_get_shader(luminance_reduce_raster.shader_version, mode);
RID framebuffer = FramebufferCacheRD::get_singleton()->get_cache(p_luminance_buffers->reduce[i]);
RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] }));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
if (final) {
RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1);
}
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
} else {
LuminanceReducePushConstant push_constant;
memset(&push_constant, 0, sizeof(LuminanceReducePushConstant));
push_constant.source_size[0] = p_source_size.x;
push_constant.source_size[1] = p_source_size.y;
push_constant.max_luminance = p_max_luminance;
push_constant.min_luminance = p_min_luminance;
push_constant.exposure_adjust = p_adjust;
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
for (int i = 0; i < p_luminance_buffers->reduce.size(); i++) {
RID shader;
if (i == 0) {
shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_READ);
RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_texture }));
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_READ]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
} else {
RD::get_singleton()->compute_list_add_barrier(compute_list); //needs barrier, wait until previous is done
if (i == p_luminance_buffers->reduce.size() - 1 && !p_set) {
shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE_WRITE);
RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current }));
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE_WRITE]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_current_texture), 2);
} else {
shader = luminance_reduce.shader.version_get_shader(luminance_reduce.shader_version, LUMINANCE_REDUCE);
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, luminance_reduce.pipelines[LUMINANCE_REDUCE]);
}
RD::Uniform u_source_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i - 1]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0);
}
RD::Uniform u_reduce_texture(RD::UNIFORM_TYPE_IMAGE, 0, p_luminance_buffers->reduce[i]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_reduce_texture), 1);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(LuminanceReducePushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.source_size[0], push_constant.source_size[1], 1);
push_constant.source_size[0] = MAX(push_constant.source_size[0] / 8, 1);
push_constant.source_size[1] = MAX(push_constant.source_size[1] / 8, 1);
}
RD::get_singleton()->compute_list_end();
}
SWAP(p_luminance_buffers->current, p_luminance_buffers->reduce.write[p_luminance_buffers->reduce.size() - 1]);
}

View File

@@ -0,0 +1,114 @@
/**************************************************************************/
/* luminance.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl.gen.h"
#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
#define RB_LUMINANCE_BUFFERS SNAME("luminance_buffers")
namespace RendererRD {
class Luminance {
private:
bool prefer_raster_effects;
enum LuminanceReduceMode {
LUMINANCE_REDUCE_READ,
LUMINANCE_REDUCE,
LUMINANCE_REDUCE_WRITE,
LUMINANCE_REDUCE_MAX
};
struct LuminanceReducePushConstant {
int32_t source_size[2];
float max_luminance;
float min_luminance;
float exposure_adjust;
float pad[3];
};
struct LuminanceReduce {
LuminanceReduceShaderRD shader;
RID shader_version;
RID pipelines[LUMINANCE_REDUCE_MAX];
} luminance_reduce;
enum LuminanceReduceRasterMode {
LUMINANCE_REDUCE_FRAGMENT_FIRST,
LUMINANCE_REDUCE_FRAGMENT,
LUMINANCE_REDUCE_FRAGMENT_FINAL,
LUMINANCE_REDUCE_FRAGMENT_MAX
};
struct LuminanceReduceRasterPushConstant {
int32_t source_size[2];
int32_t dest_size[2];
float exposure_adjust;
float min_luminance;
float max_luminance;
uint32_t pad1;
};
struct LuminanceReduceFragment {
LuminanceReduceRasterShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[LUMINANCE_REDUCE_FRAGMENT_MAX];
} luminance_reduce_raster;
public:
class LuminanceBuffers : public RenderBufferCustomDataRD {
GDCLASS(LuminanceBuffers, RenderBufferCustomDataRD);
private:
bool prefer_raster_effects;
public:
Vector<RID> reduce;
RID current;
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
virtual void free_data() override;
void set_prefer_raster_effects(bool p_prefer_raster_effects);
};
Ref<LuminanceBuffers> get_luminance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers);
RID get_current_luminance_buffer(Ref<RenderSceneBuffersRD> p_render_buffers);
void luminance_reduction(RID p_source_texture, const Size2i p_source_size, Ref<LuminanceBuffers> p_luminance_buffers, float p_min_luminance, float p_max_luminance, float p_adjust, bool p_set = false);
Luminance(bool p_prefer_raster_effects);
~Luminance();
};
} // namespace RendererRD

View File

@@ -0,0 +1,186 @@
/**************************************************************************/
/* metal_fx.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#if defined(METAL_ENABLED) && !defined(VISIONOS_ENABLED)
#define METAL_MFXTEMPORAL_ENABLED
#endif
#ifdef METAL_ENABLED
#include "spatial_upscaler.h"
#include "core/templates/paged_allocator.h"
#include "servers/rendering/renderer_scene_render.h"
#ifdef __OBJC__
@protocol MTLFXSpatialScaler;
@protocol MTLFXTemporalScaler;
#endif
namespace RendererRD {
struct MFXSpatialContext {
#ifdef __OBJC__
id<MTLFXSpatialScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MFXSpatialContext() = default;
~MFXSpatialContext();
};
class MFXSpatialEffect : public SpatialUpscaler {
struct CallbackArgs {
MFXSpatialEffect *owner;
RDD::TextureID src;
RDD::TextureID dst;
MFXSpatialContext ctx;
CallbackArgs(MFXSpatialEffect *p_owner, RDD::TextureID p_src, RDD::TextureID p_dst, MFXSpatialContext p_ctx) :
owner(p_owner), src(p_src), dst(p_dst), ctx(p_ctx) {}
static void free(CallbackArgs **p_args) {
(*p_args)->owner->args_allocator.free(*p_args);
*p_args = nullptr;
}
};
PagedAllocator<CallbackArgs, true, 16> args_allocator;
static void callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata);
public:
virtual const Span<char> get_label() const final { return "MetalFX Spatial Upscale"; }
virtual void ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) final;
virtual void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_src, RID p_dst) final;
struct CreateParams {
Vector2i input_size;
Vector2i output_size;
RD::DataFormat input_format;
RD::DataFormat output_format;
};
MFXSpatialContext *create_context(CreateParams p_params) const;
MFXSpatialEffect();
~MFXSpatialEffect();
};
#ifdef METAL_MFXTEMPORAL_ENABLED
struct MFXTemporalContext {
#ifdef __OBJC__
id<MTLFXTemporalScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MFXTemporalContext() = default;
~MFXTemporalContext();
};
class MFXTemporalEffect {
struct CallbackArgs {
MFXTemporalEffect *owner;
RDD::TextureID src;
RDD::TextureID depth;
RDD::TextureID motion;
RDD::TextureID exposure;
Vector2 jitter_offset;
RDD::TextureID dst;
MFXTemporalContext ctx;
bool reset = false;
CallbackArgs(
MFXTemporalEffect *p_owner,
RDD::TextureID p_src,
RDD::TextureID p_depth,
RDD::TextureID p_motion,
RDD::TextureID p_exposure,
Vector2 p_jitter_offset,
RDD::TextureID p_dst,
MFXTemporalContext p_ctx,
bool p_reset) :
owner(p_owner),
src(p_src),
depth(p_depth),
motion(p_motion),
exposure(p_exposure),
jitter_offset(p_jitter_offset),
dst(p_dst),
ctx(p_ctx),
reset(p_reset) {}
static void free(CallbackArgs **p_args) {
(*p_args)->owner->args_allocator.free(*p_args);
*p_args = nullptr;
}
};
PagedAllocator<CallbackArgs, true, 16> args_allocator;
static void callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata);
public:
MFXTemporalEffect();
~MFXTemporalEffect();
struct CreateParams {
Vector2i input_size;
Vector2i output_size;
RD::DataFormat input_format;
RD::DataFormat depth_format;
RD::DataFormat motion_format;
RD::DataFormat reactive_format;
RD::DataFormat output_format;
Vector2 motion_vector_scale;
};
MFXTemporalContext *create_context(CreateParams p_params) const;
struct Params {
RID src;
RID depth;
RID motion;
RID exposure;
RID dst;
Vector2 jitter_offset;
bool reset = false;
};
void process(MFXTemporalContext *p_ctx, Params p_params);
};
#endif
} //namespace RendererRD
#endif // METAL_ENABLED

View File

@@ -0,0 +1,225 @@
/**************************************************************************/
/* metal_fx.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#import "metal_fx.h"
#import "../storage_rd/render_scene_buffers_rd.h"
#import "drivers/metal/pixel_formats.h"
#import "drivers/metal/rendering_device_driver_metal.h"
#import <Metal/Metal.h>
#import <MetalFX/MetalFX.h>
using namespace RendererRD;
#pragma mark - Spatial Scaler
MFXSpatialContext::~MFXSpatialContext() {
}
MFXSpatialEffect::MFXSpatialEffect() {
}
MFXSpatialEffect::~MFXSpatialEffect() {
}
void MFXSpatialEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
obj->end();
id<MTLTexture> src_texture = rid::get(p_userdata->src);
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
__block id<MTLFXSpatialScaler> scaler = p_userdata->ctx.scaler;
scaler.colorTexture = src_texture;
scaler.outputTexture = dst_texture;
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
// TODO(sgc): add API to retain objects until the command buffer completes
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
// This block retains a reference to the scaler until the command buffer.
// completes.
scaler = nil;
}];
CallbackArgs::free(&p_userdata);
GODOT_CLANG_WARNING_POP
}
void MFXSpatialEffect::ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) {
p_render_buffers->ensure_mfx(this);
}
void MFXSpatialEffect::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_src, RID p_dst) {
MFXSpatialContext *ctx = p_render_buffers->get_mfx_spatial_context();
DEV_ASSERT(ctx); // this should have been done by the caller via ensure_context
CallbackArgs *userdata = args_allocator.alloc(
this,
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_src)),
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_dst)),
*ctx);
RD::CallbackResource res[2] = {
{ .rid = p_src, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE },
{ .rid = p_dst, .usage = RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE }
};
RD::get_singleton()->driver_callback_add((RDD::DriverCallback)MFXSpatialEffect::callback, userdata, VectorView<RD::CallbackResource>(res, 2));
}
MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const {
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL));
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
PixelFormats &pf = rdd->get_pixel_formats();
id<MTLDevice> dev = rdd->get_device();
MTLFXSpatialScalerDescriptor *desc = [MTLFXSpatialScalerDescriptor new];
desc.inputWidth = (NSUInteger)p_params.input_size.width;
desc.inputHeight = (NSUInteger)p_params.input_size.height;
desc.outputWidth = (NSUInteger)p_params.output_size.width;
desc.outputHeight = (NSUInteger)p_params.output_size.height;
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
desc.colorProcessingMode = MTLFXSpatialScalerColorProcessingModeLinear;
id<MTLFXSpatialScaler> scaler = [desc newSpatialScalerWithDevice:dev];
MFXSpatialContext *context = memnew(MFXSpatialContext);
context->scaler = scaler;
GODOT_CLANG_WARNING_POP
return context;
}
#ifdef METAL_MFXTEMPORAL_ENABLED
#pragma mark - Temporal Scaler
MFXTemporalContext::~MFXTemporalContext() {}
MFXTemporalEffect::MFXTemporalEffect() {}
MFXTemporalEffect::~MFXTemporalEffect() {}
MFXTemporalContext *MFXTemporalEffect::create_context(CreateParams p_params) const {
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL));
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
PixelFormats &pf = rdd->get_pixel_formats();
id<MTLDevice> dev = rdd->get_device();
MTLFXTemporalScalerDescriptor *desc = [MTLFXTemporalScalerDescriptor new];
desc.inputWidth = (NSUInteger)p_params.input_size.width;
desc.inputHeight = (NSUInteger)p_params.input_size.height;
desc.outputWidth = (NSUInteger)p_params.output_size.width;
desc.outputHeight = (NSUInteger)p_params.output_size.height;
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
desc.depthTextureFormat = pf.getMTLPixelFormat(p_params.depth_format);
desc.motionTextureFormat = pf.getMTLPixelFormat(p_params.motion_format);
desc.autoExposureEnabled = NO;
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
id<MTLFXTemporalScaler> scaler = [desc newTemporalScalerWithDevice:dev];
MFXTemporalContext *context = memnew(MFXTemporalContext);
context->scaler = scaler;
scaler.motionVectorScaleX = p_params.motion_vector_scale.x;
scaler.motionVectorScaleY = p_params.motion_vector_scale.y;
scaler.depthReversed = true; // Godot uses reverse Z per https://github.com/godotengine/godot/pull/88328
GODOT_CLANG_WARNING_POP
return context;
}
void MFXTemporalEffect::process(RendererRD::MFXTemporalContext *p_ctx, RendererRD::MFXTemporalEffect::Params p_params) {
CallbackArgs *userdata = args_allocator.alloc(
this,
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.src)),
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.depth)),
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.motion)),
p_params.exposure.is_valid() ? RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.exposure)) : RDD::TextureID(),
p_params.jitter_offset,
RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.dst)),
*p_ctx,
p_params.reset);
RD::CallbackResource res[3] = {
{ .rid = p_params.src, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE },
{ .rid = p_params.depth, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE },
{ .rid = p_params.dst, .usage = RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE },
};
RD::get_singleton()->driver_callback_add((RDD::DriverCallback)MFXTemporalEffect::callback, userdata, VectorView<RD::CallbackResource>(res, 3));
}
void MFXTemporalEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
obj->end();
id<MTLTexture> src_texture = rid::get(p_userdata->src);
id<MTLTexture> depth = rid::get(p_userdata->depth);
id<MTLTexture> motion = rid::get(p_userdata->motion);
id<MTLTexture> exposure = rid::get(p_userdata->exposure);
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
__block id<MTLFXTemporalScaler> scaler = p_userdata->ctx.scaler;
scaler.reset = p_userdata->reset;
scaler.colorTexture = src_texture;
scaler.depthTexture = depth;
scaler.motionTexture = motion;
scaler.exposureTexture = exposure;
scaler.jitterOffsetX = p_userdata->jitter_offset.x;
scaler.jitterOffsetY = p_userdata->jitter_offset.y;
scaler.outputTexture = dst_texture;
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
// TODO(sgc): add API to retain objects until the command buffer completes
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
// This block retains a reference to the scaler until the command buffer.
// completes.
scaler = nil;
}];
CallbackArgs::free(&p_userdata);
GODOT_CLANG_WARNING_POP
}
#endif

View File

@@ -0,0 +1,101 @@
/**************************************************************************/
/* motion_vectors_store.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "motion_vectors_store.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
namespace RendererRD {
MotionVectorsStore::MotionVectorsStore() {
Vector<String> modes;
modes.push_back("");
motion_shader.initialize(modes);
shader_version = motion_shader.version_create();
pipeline = RD::get_singleton()->compute_pipeline_create(motion_shader.version_get_shader(shader_version, 0));
}
MotionVectorsStore::~MotionVectorsStore() {
motion_shader.version_free(shader_version);
}
void MotionVectorsStore::process(Ref<RenderSceneBuffersRD> p_render_buffers,
const Projection &p_current_projection, const Transform3D &p_current_transform,
const Projection &p_previous_projection, const Transform3D &p_previous_transform) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
uint32_t view_count = p_render_buffers->get_view_count();
Size2i internal_size = p_render_buffers->get_internal_size();
PushConstant push_constant;
{
push_constant.resolution[0] = internal_size.width;
push_constant.resolution[1] = internal_size.height;
Projection correction;
correction.set_depth_correction(true, true, false);
Projection reprojection = (correction * p_previous_projection) * p_previous_transform.affine_inverse() * p_current_transform * (correction * p_current_projection).inverse();
RendererRD::MaterialStorage::store_camera(reprojection, push_constant.reprojection_matrix);
}
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RD::get_singleton()->draw_command_begin_label("Motion Vector Store");
RID shader = motion_shader.version_get_shader(shader_version, 0);
ERR_FAIL_COND(shader.is_null());
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
for (uint32_t v = 0; v < view_count; v++) {
RID velocity = p_render_buffers->get_velocity_buffer(false, v);
RID depth = p_render_buffers->get_depth_texture(v);
RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, depth }));
RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 1, velocity);
RID uniform_set = uniform_set_cache->get_cache(shader, 0, u_depth, u_velocity);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.width, internal_size.height, 1);
}
RD::get_singleton()->compute_list_end();
RD::get_singleton()->draw_command_end_label();
}
} //namespace RendererRD

View File

@@ -0,0 +1,59 @@
/**************************************************************************/
/* motion_vectors_store.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.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 MotionVectorsStore {
struct PushConstant {
float reprojection_matrix[16];
float resolution[2];
uint32_t pad[2];
};
MotionVectorsStoreShaderRD motion_shader;
RID shader_version;
RID pipeline;
public:
MotionVectorsStore();
~MotionVectorsStore();
void process(Ref<RenderSceneBuffersRD> p_render_buffers,
const Projection &p_current_projection, const Transform3D &p_current_transform,
const Projection &p_previous_projection, const Transform3D &p_previous_transform);
};
} //namespace RendererRD

View File

@@ -0,0 +1,129 @@
/**************************************************************************/
/* resolve.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "resolve.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
using namespace RendererRD;
Resolve::Resolve() {
Vector<String> resolve_modes;
resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n");
resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n#define VOXEL_GI_RESOLVE\n");
resolve_modes.push_back("\n#define MODE_RESOLVE_DEPTH\n");
resolve.shader.initialize(resolve_modes);
resolve.shader_version = resolve.shader.version_create();
for (int i = 0; i < RESOLVE_MODE_MAX; i++) {
resolve.pipelines[i] = RD::get_singleton()->compute_pipeline_create(resolve.shader.version_get_shader(resolve.shader_version, i));
}
}
Resolve::~Resolve() {
resolve.shader.version_free(resolve.shader_version);
}
void Resolve::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
ResolvePushConstant push_constant;
push_constant.screen_size[0] = p_screen_size.x;
push_constant.screen_size[1] = p_screen_size.y;
push_constant.samples = p_samples;
// setup our uniforms
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_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_depth }));
RD::Uniform u_source_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_source_normal_roughness }));
RD::Uniform u_dest_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_dest_depth }));
RD::Uniform u_dest_normal_roughness(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_dest_normal_roughness }));
ResolveMode mode = p_source_voxel_gi.is_valid() ? RESOLVE_MODE_GI_VOXEL_GI : RESOLVE_MODE_GI;
RID shader = resolve.shader.version_get_shader(resolve.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[mode]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_depth, u_source_normal_roughness), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_depth, u_dest_normal_roughness), 1);
if (p_source_voxel_gi.is_valid()) {
RD::Uniform u_source_voxel_gi(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_voxel_gi }));
RD::Uniform u_dest_voxel_gi(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_voxel_gi);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_source_voxel_gi), 2);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_voxel_gi), 3);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
RD::get_singleton()->compute_list_end();
}
void Resolve::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
ResolvePushConstant push_constant;
push_constant.screen_size[0] = p_screen_size.x;
push_constant.screen_size[1] = p_screen_size.y;
push_constant.samples = p_samples;
// setup our uniforms
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_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_depth }));
RD::Uniform u_dest_depth(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_depth);
ResolveMode mode = RESOLVE_MODE_DEPTH;
RID shader = resolve.shader.version_get_shader(resolve.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[mode]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_depth), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_depth), 1);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
RD::get_singleton()->compute_list_end();
}

View File

@@ -0,0 +1,67 @@
/**************************************************************************/
/* resolve.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/shaders/effects/resolve.glsl.gen.h"
namespace RendererRD {
class Resolve {
private:
struct ResolvePushConstant {
int32_t screen_size[2];
int32_t samples;
uint32_t pad;
};
enum ResolveMode {
RESOLVE_MODE_GI,
RESOLVE_MODE_GI_VOXEL_GI,
RESOLVE_MODE_DEPTH,
RESOLVE_MODE_MAX
};
struct ResolveShader {
ResolvePushConstant push_constant;
ResolveShaderRD shader;
RID shader_version;
RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels
} resolve;
public:
Resolve();
~Resolve();
void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples);
void resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples);
};
} // namespace RendererRD

View File

@@ -0,0 +1,79 @@
/**************************************************************************/
/* roughness_limiter.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "roughness_limiter.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
using namespace RendererRD;
RoughnessLimiter::RoughnessLimiter() {
// Initialize roughness limiter
Vector<String> shader_modes;
shader_modes.push_back("");
shader.initialize(shader_modes);
shader_version = shader.version_create();
pipeline = RD::get_singleton()->compute_pipeline_create(shader.version_get_shader(shader_version, 0));
}
RoughnessLimiter::~RoughnessLimiter() {
shader.version_free(shader_version);
}
void RoughnessLimiter::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
push_constant.screen_size[0] = p_size.x;
push_constant.screen_size[1] = p_size.y;
push_constant.curve = p_curve;
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
RID rl_shader = shader.version_get_shader(shader_version, 0);
RD::Uniform u_source_normal(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_normal }));
RD::Uniform u_roughness(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_roughness }));
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(rl_shader, 0, u_source_normal), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(rl_shader, 1, u_roughness), 1);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RoughnessLimiterPushConstant)); //not used but set anyway
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_size.x, p_size.y, 1);
RD::get_singleton()->compute_list_end();
}

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* roughness_limiter.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/shaders/effects/roughness_limiter.glsl.gen.h"
namespace RendererRD {
// Note, this logic is unused at the time of writing. It should be re-incorporated into the renderer at some point.
class RoughnessLimiter {
private:
struct RoughnessLimiterPushConstant {
int32_t screen_size[2];
float curve;
uint32_t pad;
};
RoughnessLimiterPushConstant push_constant;
RoughnessLimiterShaderRD shader;
RID shader_version;
RID pipeline;
protected:
public:
RoughnessLimiter();
~RoughnessLimiter();
void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
};
} // namespace RendererRD

View File

@@ -0,0 +1,273 @@
/**************************************************************************/
/* smaa.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "smaa.h"
#include "core/config/project_settings.h"
#include "core/io/image_loader.h"
#include "servers/rendering/renderer_rd/effects/smaa_area_tex.gen.h"
#include "servers/rendering/renderer_rd/effects/smaa_search_tex.gen.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;
SMAA::SMAA() {
{
// Initialize edge detection.
Vector<String> smaa_modes;
smaa_modes.push_back("\n");
smaa.edge_shader.initialize(smaa_modes);
smaa.edge_shader_version = smaa.edge_shader.version_create();
RD::PipelineDepthStencilState stencil_state = RD::PipelineDepthStencilState();
stencil_state.enable_stencil = true;
stencil_state.back_op.reference = 0xff;
stencil_state.back_op.write_mask = 0xff;
stencil_state.back_op.compare_mask = 0xff;
stencil_state.back_op.pass = RD::STENCIL_OP_REPLACE;
stencil_state.front_op.reference = 0xff;
stencil_state.front_op.write_mask = 0xff;
stencil_state.front_op.compare_mask = 0xff;
stencil_state.front_op.pass = RD::STENCIL_OP_REPLACE;
for (int i = SMAA_EDGE_DETECTION_COLOR; i <= SMAA_EDGE_DETECTION_COLOR; i++) {
smaa.pipelines[i].setup(smaa.edge_shader.version_get_shader(smaa.edge_shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
}
edge_detection_threshold = GLOBAL_GET("rendering/anti_aliasing/quality/smaa_edge_detection_threshold");
}
{
// Initialize weight calculation.
Vector<String> smaa_modes;
smaa_modes.push_back("\n");
smaa.weight_shader.initialize(smaa_modes);
smaa.weight_shader_version = smaa.weight_shader.version_create();
RD::PipelineDepthStencilState stencil_state;
stencil_state.enable_stencil = true;
stencil_state.back_op.reference = 0xff;
stencil_state.back_op.compare_mask = 0xff;
stencil_state.back_op.compare = RD::COMPARE_OP_EQUAL;
stencil_state.front_op.reference = 0xff;
stencil_state.front_op.compare_mask = 0xff;
stencil_state.front_op.compare = RD::COMPARE_OP_EQUAL;
for (int i = SMAA_WEIGHT_FULL; i <= SMAA_WEIGHT_FULL; i++) {
smaa.pipelines[i].setup(smaa.weight_shader.version_get_shader(smaa.weight_shader_version, i - SMAA_WEIGHT_FULL), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), stencil_state, RD::PipelineColorBlendState::create_disabled(), 0);
}
}
{
// Initialize color blending.
Vector<String> smaa_modes;
smaa_modes.push_back("\n");
smaa.blend_shader.initialize(smaa_modes);
smaa.blend_shader_version = smaa.blend_shader.version_create();
for (int i = SMAA_BLENDING; i <= SMAA_BLENDING; i++) {
smaa.pipelines[i].setup(smaa.blend_shader.version_get_shader(smaa.blend_shader_version, i - SMAA_BLENDING), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
}
{
// Initialize SearchTex.
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R8_UNORM;
tf.width = SEARCHTEX_WIDTH;
tf.height = SEARCHTEX_HEIGHT;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
smaa.search_tex = RD::get_singleton()->texture_create(tf, RD::TextureView(), Vector<Vector<unsigned char>>{ Image(search_tex_png).get_data() });
}
{
// Initialize AreaTex.
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R8G8_UNORM;
tf.width = AREATEX_WIDTH;
tf.height = AREATEX_HEIGHT;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
smaa.area_tex = RD::get_singleton()->texture_create(tf, RD::TextureView(), Vector<Vector<unsigned char>>{ Image(area_tex_png).get_data() });
}
{
// Find smallest stencil texture format.
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D16_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
smaa.stencil_format = RD::DATA_FORMAT_D16_UNORM_S8_UINT;
} else if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
smaa.stencil_format = RD::DATA_FORMAT_D24_UNORM_S8_UINT;
} else {
smaa.stencil_format = RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
}
}
}
SMAA::~SMAA() {
RD::get_singleton()->free(smaa.search_tex);
RD::get_singleton()->free(smaa.area_tex);
smaa.edge_shader.version_free(smaa.edge_shader_version);
smaa.weight_shader.version_free(smaa.weight_shader_version);
smaa.blend_shader.version_free(smaa.blend_shader_version);
}
void SMAA::allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers) {
Size2i full_size = p_render_buffers->get_internal_size();
// As we're not clearing these, and render buffers will return the cached texture if it already exists,
// we don't first check has_texture here.
p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_EDGES, RD::DATA_FORMAT_R8G8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_BLEND, RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
p_render_buffers->create_texture(RB_SCOPE_SMAA, RB_STENCIL, smaa.stencil_format, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, full_size, 1, 1, true, true);
}
void SMAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
memset(&smaa.edge_push_constant, 0, sizeof(SMAAEdgePushConstant));
memset(&smaa.weight_push_constant, 0, sizeof(SMAAWeightPushConstant));
memset(&smaa.blend_push_constant, 0, sizeof(SMAABlendPushConstant));
Size2i size = p_render_buffers->get_internal_size();
Size2 inv_size = Size2(1.0f / (float)size.x, 1.0f / (float)size.y);
smaa.edge_push_constant.inv_size[0] = inv_size.x;
smaa.edge_push_constant.inv_size[1] = inv_size.y;
smaa.edge_push_constant.threshold = edge_detection_threshold;
smaa.weight_push_constant.inv_size[0] = inv_size.x;
smaa.weight_push_constant.inv_size[1] = inv_size.y;
smaa.weight_push_constant.size[0] = size.x;
smaa.weight_push_constant.size[1] = size.y;
smaa.blend_push_constant.inv_size[0] = inv_size.x;
smaa.blend_push_constant.inv_size[1] = inv_size.y;
if (debanding_mode == DEBANDING_MODE_8_BIT) {
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_8_BIT_DEBANDING;
} else if (debanding_mode == DEBANDING_MODE_10_BIT) {
smaa.blend_push_constant.flags |= SMAA_BLEND_FLAG_USE_10_BIT_DEBANDING;
}
RID linear_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
allocate_render_targets(p_render_buffers);
RID edges_tex = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_EDGES);
RID blend_tex = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_BLEND);
RID stencil_buffer = p_render_buffers->get_texture(RB_SCOPE_SMAA, RB_STENCIL);
RID edges_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(edges_tex, stencil_buffer);
RID blend_framebuffer = FramebufferCacheRD::get_singleton()->get_cache(blend_tex, stencil_buffer);
RD::Uniform u_source_color;
u_source_color.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_source_color.binding = 0;
u_source_color.append_id(linear_sampler);
u_source_color.append_id(p_source_color);
RD::Uniform u_edges_texture;
u_edges_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_edges_texture.binding = 0;
u_edges_texture.append_id(linear_sampler);
u_edges_texture.append_id(edges_tex);
RD::Uniform u_area_texture;
u_area_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_area_texture.binding = 0;
u_area_texture.append_id(linear_sampler);
u_area_texture.append_id(smaa.area_tex);
RD::Uniform u_search_texture;
u_search_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_search_texture.binding = 1;
u_search_texture.append_id(linear_sampler);
u_search_texture.append_id(smaa.search_tex);
RD::Uniform u_blend_texture;
u_blend_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_blend_texture.binding = 0;
u_blend_texture.append_id(linear_sampler);
u_blend_texture.append_id(blend_tex);
{
int mode = SMAA_EDGE_DETECTION_COLOR;
RID shader = smaa.edge_shader.version_get_shader(smaa.edge_shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(edges_framebuffer, RD::DRAW_CLEAR_COLOR_0 | RD::DRAW_CLEAR_STENCIL, Vector<Color>({ Color(0, 0, 0, 0) }), 1.0f, 0);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(edges_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.edge_push_constant, sizeof(SMAAEdgePushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
{
int mode = SMAA_WEIGHT_FULL;
RID shader = smaa.weight_shader.version_get_shader(smaa.weight_shader_version, mode - SMAA_WEIGHT_FULL);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(blend_framebuffer, RD::DRAW_CLEAR_COLOR_0, Vector<Color>({ Color(0, 0, 0, 0) }));
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(blend_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_edges_texture), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_area_texture, u_search_texture), 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.weight_push_constant, sizeof(SMAAWeightPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
{
int mode = SMAA_BLENDING;
RID shader = smaa.blend_shader.version_get_shader(smaa.blend_shader_version, mode - SMAA_BLENDING);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::DRAW_IGNORE_COLOR_0);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, smaa.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_blend_texture), 1);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &smaa.blend_push_constant, sizeof(SMAABlendPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
}

View File

@@ -0,0 +1,121 @@
/**************************************************************************/
/* smaa.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/smaa_blending.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/smaa_edge_detection.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/effects/smaa_weight_calculation.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"
#define RB_SCOPE_SMAA SNAME("rb_smaa")
#define RB_EDGES SNAME("edges")
#define RB_BLEND SNAME("blend")
#define RB_STENCIL SNAME("stencil")
namespace RendererRD {
class SMAA {
private:
enum SMAAMode {
SMAA_EDGE_DETECTION_COLOR,
SMAA_WEIGHT_FULL,
SMAA_BLENDING,
SMAA_MAX,
};
struct SMAAEdgePushConstant {
float inv_size[2];
float threshold;
float pad;
};
struct SMAAWeightPushConstant {
float inv_size[2];
uint32_t size[2];
float subsample_indices[4];
};
struct SMAABlendPushConstant {
float inv_size[2];
uint32_t flags;
float pad;
};
enum SMAABlendFlags {
SMAA_BLEND_FLAG_USE_8_BIT_DEBANDING = (1 << 0),
SMAA_BLEND_FLAG_USE_10_BIT_DEBANDING = (1 << 1),
};
struct SMAAEffect {
SMAAEdgePushConstant edge_push_constant;
SmaaEdgeDetectionShaderRD edge_shader;
RID edge_shader_version;
SMAAWeightPushConstant weight_push_constant;
SmaaWeightCalculationShaderRD weight_shader;
RID weight_shader_version;
SMAABlendPushConstant blend_push_constant;
SmaaBlendingShaderRD blend_shader;
RID blend_shader_version;
RID search_tex;
RID area_tex;
RD::DataFormat stencil_format;
PipelineCacheRD pipelines[SMAA_MAX];
} smaa;
float edge_detection_threshold;
public:
SMAA();
~SMAA();
void allocate_render_targets(Ref<RenderSceneBuffersRD> p_render_buffers);
void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_color, RID p_dst_framebuffer);
enum DebandingMode {
DEBANDING_MODE_DISABLED,
DEBANDING_MODE_8_BIT,
DEBANDING_MODE_10_BIT,
};
DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
};
} // namespace RendererRD

View File

@@ -0,0 +1,122 @@
/**************************************************************************/
/* sort_effects.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "sort_effects.h"
// #include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
using namespace RendererRD;
SortEffects::SortEffects() {
Vector<String> sort_modes;
sort_modes.push_back("\n#define MODE_SORT_BLOCK\n");
sort_modes.push_back("\n#define MODE_SORT_STEP\n");
sort_modes.push_back("\n#define MODE_SORT_INNER\n");
shader.initialize(sort_modes);
shader_version = shader.version_create();
for (int i = 0; i < SORT_MODE_MAX; i++) {
pipelines[i] = RD::get_singleton()->compute_pipeline_create(shader.version_get_shader(shader_version, i));
}
}
SortEffects::~SortEffects() {
shader.version_free(shader_version);
}
void SortEffects::sort_buffer(RID p_uniform_set, int p_size) {
PushConstant push_constant;
push_constant.total_elements = p_size;
bool done = true;
int numThreadGroups = ((p_size - 1) >> 9) + 1;
if (numThreadGroups > 1) {
done = false;
}
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[SORT_MODE_BLOCK]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_uniform_set, 1);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
int presorted = 512;
while (!done) {
RD::get_singleton()->compute_list_add_barrier(compute_list);
done = true;
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[SORT_MODE_STEP]);
numThreadGroups = 0;
if (p_size > presorted) {
if (p_size > presorted * 2) {
done = false;
}
int pow2 = presorted;
while (pow2 < p_size) {
pow2 *= 2;
}
numThreadGroups = pow2 >> 9;
}
unsigned int nMergeSize = presorted * 2;
for (unsigned int nMergeSubSize = nMergeSize >> 1; nMergeSubSize > 256; nMergeSubSize = nMergeSubSize >> 1) {
push_constant.job_params[0] = nMergeSubSize;
if (nMergeSubSize == nMergeSize >> 1) {
push_constant.job_params[1] = (2 * nMergeSubSize - 1);
push_constant.job_params[2] = -1;
} else {
push_constant.job_params[1] = nMergeSubSize;
push_constant.job_params[2] = 1;
}
push_constant.job_params[3] = 0;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
RD::get_singleton()->compute_list_add_barrier(compute_list);
}
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[SORT_MODE_INNER]);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, numThreadGroups, 1, 1);
presorted *= 2;
}
RD::get_singleton()->compute_list_end();
}

View File

@@ -0,0 +1,65 @@
/**************************************************************************/
/* sort_effects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/shader_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/sort.glsl.gen.h"
namespace RendererRD {
class SortEffects {
private:
enum SortMode {
SORT_MODE_BLOCK,
SORT_MODE_STEP,
SORT_MODE_INNER,
SORT_MODE_MAX
};
struct PushConstant {
uint32_t total_elements;
uint32_t pad[3];
int32_t job_params[4];
};
SortShaderRD shader;
RID shader_version;
RID pipelines[SORT_MODE_MAX];
protected:
public:
SortEffects();
~SortEffects();
void sort_buffer(RID p_uniform_set, int p_size);
};
} // namespace RendererRD

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* spatial_upscaler.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "core/object/ref_counted.h"
class RenderSceneBuffersRD;
class SpatialUpscaler {
public:
virtual const Span<char> get_label() const = 0;
virtual void ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
virtual void process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) = 0;
SpatialUpscaler() = default;
virtual ~SpatialUpscaler() = default;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,528 @@
/**************************************************************************/
/* ss_effects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#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_server.h"
#define RB_SCOPE_SSDS SNAME("rb_ssds")
#define RB_SCOPE_SSIL SNAME("rb_ssil")
#define RB_SCOPE_SSAO SNAME("rb_ssao")
#define RB_SCOPE_SSR SNAME("rb_ssr")
#define RB_LINEAR_DEPTH SNAME("linear_depth")
#define RB_FINAL SNAME("final")
#define RB_LAST_FRAME SNAME("last_frame")
#define RB_DEINTERLEAVED SNAME("deinterleaved")
#define RB_DEINTERLEAVED_PONG SNAME("deinterleaved_pong")
#define RB_EDGES SNAME("edges")
#define RB_IMPORTANCE_MAP SNAME("importance_map")
#define RB_IMPORTANCE_PONG SNAME("importance_pong")
#define RB_DEPTH_SCALED SNAME("depth_scaled")
#define RB_NORMAL_SCALED SNAME("normal_scaled")
#define RB_BLUR_RADIUS SNAME("blur_radius")
#define RB_INTERMEDIATE SNAME("intermediate")
#define RB_OUTPUT SNAME("output")
class RenderSceneBuffersRD;
namespace RendererRD {
class SSEffects {
private:
static SSEffects *singleton;
public:
static SSEffects *get_singleton() { return singleton; }
SSEffects();
~SSEffects();
/* SS Downsampler */
void downsample_depth(Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_view, 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;
};
struct SSILSettings {
float radius = 1.0;
float intensity = 2.0;
float sharpness = 0.98;
float normal_rejection = 1.0;
Size2i full_screen_size;
};
void ssil_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSILRenderBuffers &p_ssil_buffers, const SSILSettings &p_settings);
void screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_render_buffers, SSILRenderBuffers &p_ssil_buffers, uint32_t p_view, RID p_normal_buffer, const Projection &p_projection, const Projection &p_last_projection, const SSILSettings &p_settings);
/* 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;
};
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;
};
void ssao_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORenderBuffers &p_ssao_buffers, const SSAOSettings &p_settings);
void generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORenderBuffers &p_ssao_buffers, uint32_t p_view, RID p_normal_buffer, const Projection &p_projection, const SSAOSettings &p_settings);
/* 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;
};
void ssr_allocate_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RenderingDevice::DataFormat p_color_format);
void screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffers, SSRRenderBuffers &p_ssr_buffers, const RID *p_normal_roughness_slices, const RID *p_metallic_slices, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const Projection *p_projections, const Vector3 *p_eye_offsets);
/* 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;
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 RID *p_ssil_slices, const 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 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

View File

@@ -0,0 +1,124 @@
/**************************************************************************/
/* taa.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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::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 = 2.5f; // If velocity changes by less than this amount of texels we can retain the accumulation buffer.
push_constant.disocclusion_scale = 0.01f; // Scale the weight of this pixel calculated as (change in velocity - threshold) * scale.
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();
}

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* taa.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl.gen.h"
#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
namespace RendererRD {
class TAA {
public:
TAA();
~TAA();
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

View File

@@ -0,0 +1,266 @@
/**************************************************************************/
/* tone_mapper.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "tone_mapper.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/uniform_set_cache_rd.h"
using namespace RendererRD;
ToneMapper::ToneMapper() {
{
// Initialize tonemapper
Vector<String> tonemap_modes;
tonemap_modes.push_back("\n");
tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n");
tonemap_modes.push_back("\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define SUBPASS\n");
tonemap_modes.push_back("\n#define SUBPASS\n#define USE_1D_LUT\n");
// multiview versions of our shaders
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n");
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n");
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n");
tonemap_modes.push_back("\n#define USE_MULTIVIEW\n#define SUBPASS\n#define USE_1D_LUT\n");
tonemap.shader.initialize(tonemap_modes);
if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
tonemap.shader.set_variant_enabled(TONEMAP_MODE_NORMAL_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW, false);
}
tonemap.shader_version = tonemap.shader.version_create();
for (int i = 0; i < TONEMAP_MODE_MAX; i++) {
if (tonemap.shader.is_variant_enabled(i)) {
tonemap.pipelines[i].setup(tonemap.shader.version_get_shader(tonemap.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
} else {
tonemap.pipelines[i].clear();
}
}
}
}
ToneMapper::~ToneMapper() {
tonemap.shader.version_free(tonemap.shader_version);
}
void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &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);
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
tonemap.push_constant.glow_intensity = p_settings.glow_intensity;
tonemap.push_constant.glow_map_strength = p_settings.glow_map_strength;
tonemap.push_constant.glow_levels[0] = p_settings.glow_levels[0]; // clean this up to just pass by pointer or something
tonemap.push_constant.glow_levels[1] = p_settings.glow_levels[1];
tonemap.push_constant.glow_levels[2] = p_settings.glow_levels[2];
tonemap.push_constant.glow_levels[3] = p_settings.glow_levels[3];
tonemap.push_constant.glow_levels[4] = p_settings.glow_levels[4];
tonemap.push_constant.glow_levels[5] = p_settings.glow_levels[5];
tonemap.push_constant.glow_levels[6] = p_settings.glow_levels[6];
tonemap.push_constant.glow_texture_size[0] = p_settings.glow_texture_size.x;
tonemap.push_constant.glow_texture_size[1] = p_settings.glow_texture_size.y;
tonemap.push_constant.glow_mode = p_settings.glow_mode;
int mode = p_settings.glow_use_bicubic_upscale ? TONEMAP_MODE_BICUBIC_GLOW_FILTER : TONEMAP_MODE_NORMAL;
if (p_settings.use_1d_color_correction) {
mode += 2;
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0;
if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
}
tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0;
if (p_settings.view_count > 1) {
// Use USE_MULTIVIEW versions
mode += 6;
}
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::Uniform u_source_color(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_color }));
RD::Uniform u_exposure_texture;
u_exposure_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_exposure_texture.binding = 0;
u_exposure_texture.append_id(default_sampler);
u_exposure_texture.append_id(p_settings.exposure_texture);
RD::Uniform u_glow_texture;
u_glow_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_glow_texture.binding = 0;
u_glow_texture.append_id(default_mipmap_sampler);
u_glow_texture.append_id(p_settings.glow_texture);
RD::Uniform u_glow_map;
u_glow_map.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_glow_map.binding = 1;
u_glow_map.append_id(default_mipmap_sampler);
u_glow_map.append_id(p_settings.glow_map);
RD::Uniform u_color_correction_texture;
u_color_correction_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_color_correction_texture.binding = 0;
u_color_correction_texture.append_id(default_sampler);
u_color_correction_texture.append_id(p_settings.color_correction_texture);
RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &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);
memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant));
tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0;
tonemap.push_constant.bcs[0] = p_settings.brightness;
tonemap.push_constant.bcs[1] = p_settings.contrast;
tonemap.push_constant.bcs[2] = p_settings.saturation;
ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses.");
tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0;
int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS;
if (p_settings.view_count > 1) {
// Use USE_MULTIVIEW versions
mode += 6;
}
tonemap.push_constant.tonemapper = p_settings.tonemap_mode;
tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0;
tonemap.push_constant.exposure = p_settings.exposure;
tonemap.push_constant.white = p_settings.white;
tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale;
tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0;
if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_8_BIT) {
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_8_BIT_DEBANDING;
} else if (p_settings.debanding_mode == TonemapSettings::DEBANDING_MODE_10_BIT) {
tonemap.push_constant.flags |= TONEMAP_FLAG_USE_10_BIT_DEBANDING;
}
tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier;
tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 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::Uniform u_source_color;
u_source_color.uniform_type = RD::UNIFORM_TYPE_INPUT_ATTACHMENT;
u_source_color.binding = 0;
u_source_color.append_id(p_source_color);
RD::Uniform u_exposure_texture;
u_exposure_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_exposure_texture.binding = 0;
u_exposure_texture.append_id(default_sampler);
u_exposure_texture.append_id(p_settings.exposure_texture);
RD::Uniform u_glow_texture;
u_glow_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_glow_texture.binding = 0;
u_glow_texture.append_id(default_mipmap_sampler);
u_glow_texture.append_id(p_settings.glow_texture);
RD::Uniform u_glow_map;
u_glow_map.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_glow_map.binding = 1;
u_glow_map.append_id(default_mipmap_sampler);
u_glow_map.append_id(p_settings.glow_map);
RD::Uniform u_color_correction_texture;
u_color_correction_texture.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
u_color_correction_texture.binding = 0;
u_color_correction_texture.append_id(default_sampler);
u_color_correction_texture.append_id(p_settings.color_correction_texture);
RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0);
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3);
RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant));
RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u);
}

View File

@@ -0,0 +1,161 @@
/**************************************************************************/
/* tone_mapper.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/tonemap.glsl.gen.h"
#include "servers/rendering_server.h"
namespace RendererRD {
class ToneMapper {
private:
enum TonemapMode {
TONEMAP_MODE_NORMAL,
TONEMAP_MODE_BICUBIC_GLOW_FILTER,
TONEMAP_MODE_1D_LUT,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT,
TONEMAP_MODE_SUBPASS,
TONEMAP_MODE_SUBPASS_1D_LUT,
TONEMAP_MODE_NORMAL_MULTIVIEW,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW,
TONEMAP_MODE_1D_LUT_MULTIVIEW,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW,
TONEMAP_MODE_SUBPASS_MULTIVIEW,
TONEMAP_MODE_SUBPASS_1D_LUT_MULTIVIEW,
TONEMAP_MODE_MAX
};
enum {
TONEMAP_FLAG_USE_BCS = (1 << 0),
TONEMAP_FLAG_USE_GLOW = (1 << 1),
TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2),
TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3),
TONEMAP_FLAG_USE_FXAA = (1 << 4),
TONEMAP_FLAG_USE_8_BIT_DEBANDING = (1 << 5),
TONEMAP_FLAG_USE_10_BIT_DEBANDING = (1 << 6),
TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 7),
};
struct TonemapPushConstant {
float bcs[3]; // 12 - 12
uint32_t flags; // 4 - 16
float pixel_size[2]; // 8 - 24
uint32_t tonemapper; // 4 - 28
uint32_t pad; // 4 - 32
uint32_t glow_texture_size[2]; // 8 - 40
float glow_intensity; // 4 - 44
float glow_map_strength; // 4 - 48
uint32_t glow_mode; // 4 - 52
float glow_levels[7]; // 28 - 80
float exposure; // 4 - 84
float white; // 4 - 88
float auto_exposure_scale; // 4 - 92
float luminance_multiplier; // 4 - 96
};
/* tonemap actually writes to a framebuffer, which is
* better to do using the raster pipeline rather than
* compute, as that framebuffer might be in different formats
*/
struct Tonemap {
TonemapPushConstant push_constant;
TonemapShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[TONEMAP_MODE_MAX];
} tonemap;
public:
ToneMapper();
~ToneMapper();
struct TonemapSettings {
bool use_glow = false;
enum GlowMode {
GLOW_MODE_ADD,
GLOW_MODE_SCREEN,
GLOW_MODE_SOFTLIGHT,
GLOW_MODE_REPLACE,
GLOW_MODE_MIX
};
GlowMode glow_mode = GLOW_MODE_ADD;
float glow_intensity = 1.0;
float glow_map_strength = 0.0f;
float glow_levels[7] = { 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0 };
Vector2i glow_texture_size;
bool glow_use_bicubic_upscale = false;
RID glow_texture;
RID glow_map;
RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR;
float exposure = 1.0;
float white = 1.0;
bool use_auto_exposure = false;
float auto_exposure_scale = 0.5;
RID exposure_texture;
float luminance_multiplier = 1.0;
bool use_bcs = false;
float brightness = 1.0;
float contrast = 1.0;
float saturation = 1.0;
bool use_color_correction = false;
bool use_1d_color_correction = false;
RID color_correction_texture;
bool use_fxaa = false;
enum DebandingMode {
DEBANDING_MODE_DISABLED,
DEBANDING_MODE_8_BIT,
DEBANDING_MODE_10_BIT,
};
DebandingMode debanding_mode = DEBANDING_MODE_DISABLED;
Vector2i texture_size;
uint32_t view_count = 1;
bool convert_to_srgb = false;
};
void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
void tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_color, RD::FramebufferFormatID p_dst_format_id, const TonemapSettings &p_settings);
};
} // namespace RendererRD

View File

@@ -0,0 +1,156 @@
/**************************************************************************/
/* vrs.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "vrs.h"
#include "../renderer_compositor_rd.h"
#include "../storage_rd/texture_storage.h"
#include "../uniform_set_cache_rd.h"
#ifndef XR_DISABLED
#include "servers/xr_server.h"
#endif // XR_DISABLED
using namespace RendererRD;
VRS::VRS() {
{
Vector<String> vrs_modes;
vrs_modes.push_back("\n"); // VRS_DEFAULT
vrs_modes.push_back("\n#define USE_MULTIVIEW\n"); // VRS_MULTIVIEW
vrs_modes.push_back("\n#define SPLIT_RG\n"); // VRS_RG
vrs_modes.push_back("\n#define SPLIT_RG\n#define USE_MULTIVIEW\n"); // VRS_RG_MULTIVIEW
vrs_shader.shader.initialize(vrs_modes);
if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
vrs_shader.shader.set_variant_enabled(VRS_MULTIVIEW, false);
}
vrs_shader.shader_version = vrs_shader.shader.version_create();
//use additive
for (int i = 0; i < VRS_MAX; i++) {
if (vrs_shader.shader.is_variant_enabled(i)) {
vrs_shader.pipelines[i].setup(vrs_shader.shader.version_get_shader(vrs_shader.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
} else {
vrs_shader.pipelines[i].clear();
}
}
}
}
VRS::~VRS() {
vrs_shader.shader.version_free(vrs_shader.shader_version);
}
void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multiview) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
// setup our uniforms
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, 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 }));
int mode = 0;
VRSPushConstant push_constant = {};
bool uses_rg_format = RD::get_singleton()->vrs_get_format() == RD::DATA_FORMAT_R8G8_UNORM;
if (uses_rg_format) {
mode = p_multiview ? VRS_RG_MULTIVIEW : VRS_RG;
} else {
mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT;
// Default to 4x4 as it's not possible to query the max fragment size from RenderingDevice. This can be improved to use the largest size
// available if this code is moved over to RenderingDevice at some point.
push_constant.max_texel_factor = 2.0;
}
RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[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_source_rd_texture), 0);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(VRSPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u);
RD::get_singleton()->draw_list_end();
}
Size2i VRS::get_vrs_texture_size(const Size2i p_base_size) const {
Size2i vrs_texel_size = RD::get_singleton()->vrs_get_texel_size();
return Size2i((p_base_size.x + vrs_texel_size.x - 1) / vrs_texel_size.x, (p_base_size.y + vrs_texel_size.y - 1) / vrs_texel_size.y);
}
void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
TextureStorage *texture_storage = TextureStorage::get_singleton();
RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(p_render_target);
RS::ViewportVRSUpdateMode vrs_update_mode = texture_storage->render_target_get_vrs_update_mode(p_render_target);
if (vrs_mode != RS::VIEWPORT_VRS_DISABLED && vrs_update_mode != RS::VIEWPORT_VRS_UPDATE_DISABLED) {
RD::get_singleton()->draw_command_begin_label("VRS Setup");
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()) {
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(rd_texture, p_vrs_fb, layers > 1);
}
}
#ifndef XR_DISABLED
} else if (vrs_mode == RS::VIEWPORT_VRS_XR) {
Ref<XRInterface> interface = XRServer::get_singleton()->get_primary_interface();
if (interface.is_valid() && interface->get_vrs_texture_format() == XRInterface::XR_VRS_TEXTURE_FORMAT_UNIFIED) {
RID vrs_texture = interface->get_vrs_texture();
if (vrs_texture.is_valid()) {
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(rd_texture, p_vrs_fb, layers > 1);
}
}
}
#endif // XR_DISABLED
}
if (vrs_update_mode == RS::VIEWPORT_VRS_UPDATE_ONCE) {
texture_storage->render_target_set_vrs_update_mode(p_render_target, RS::VIEWPORT_VRS_UPDATE_DISABLED);
}
RD::get_singleton()->draw_command_end_label();
}
}

View File

@@ -0,0 +1,72 @@
/**************************************************************************/
/* vrs.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#pragma once
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/shaders/effects/vrs.glsl.gen.h"
namespace RendererRD {
class VRS {
private:
enum VRSMode {
VRS_DEFAULT,
VRS_MULTIVIEW,
VRS_RG,
VRS_RG_MULTIVIEW,
VRS_MAX,
};
struct VRSPushConstant {
float max_texel_factor; // 4x8, 8x4 and 8x8 are only available on some GPUs.
float res1;
float res2;
float res3;
};
struct VRSShader {
// VRSPushConstant push_constant;
VrsShaderRD shader;
RID shader_version;
PipelineCacheRD pipelines[VRS_MAX];
} vrs_shader;
public:
VRS();
~VRS();
void copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multiview = false);
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