feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
13
engine/servers/rendering/renderer_rd/SCsub
Normal file
13
engine/servers/rendering/renderer_rd/SCsub
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
|
||||
SConscript("effects/SCsub")
|
||||
SConscript("environment/SCsub")
|
||||
SConscript("forward_clustered/SCsub")
|
||||
SConscript("forward_mobile/SCsub")
|
||||
SConscript("shaders/SCsub")
|
||||
SConscript("spirv-reflect/SCsub")
|
||||
SConscript("storage_rd/SCsub")
|
||||
573
engine/servers/rendering/renderer_rd/cluster_builder_rd.cpp
Normal file
573
engine/servers/rendering/renderer_rd/cluster_builder_rd.cpp
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
/**************************************************************************/
|
||||
/* cluster_builder_rd.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 "cluster_builder_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/rendering_server_globals.h"
|
||||
|
||||
ClusterBuilderSharedDataRD::ClusterBuilderSharedDataRD() {
|
||||
RD::VertexFormatID vertex_format;
|
||||
|
||||
{
|
||||
Vector<RD::VertexAttribute> attributes;
|
||||
{
|
||||
RD::VertexAttribute va;
|
||||
va.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
|
||||
va.stride = sizeof(float) * 3;
|
||||
attributes.push_back(va);
|
||||
}
|
||||
vertex_format = RD::get_singleton()->vertex_format_create(attributes);
|
||||
}
|
||||
|
||||
{
|
||||
RD::FramebufferFormatID fb_format;
|
||||
RD::PipelineColorBlendState blend_state;
|
||||
String defines;
|
||||
if (RD::get_singleton()->has_feature(RD::SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS)) {
|
||||
fb_format = RD::get_singleton()->framebuffer_format_create_empty();
|
||||
blend_state = RD::PipelineColorBlendState::create_disabled();
|
||||
} else {
|
||||
Vector<RD::AttachmentFormat> afs;
|
||||
afs.push_back(RD::AttachmentFormat());
|
||||
afs.write[0].usage_flags = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
fb_format = RD::get_singleton()->framebuffer_format_create(afs);
|
||||
blend_state = RD::PipelineColorBlendState::create_blend();
|
||||
defines = "\n#define USE_ATTACHMENT\n";
|
||||
}
|
||||
|
||||
RD::PipelineRasterizationState rasterization_state;
|
||||
rasterization_state.enable_depth_clamp = true;
|
||||
Vector<String> versions;
|
||||
versions.push_back("");
|
||||
cluster_render.cluster_render_shader.initialize(versions, defines);
|
||||
cluster_render.shader_version = cluster_render.cluster_render_shader.version_create();
|
||||
cluster_render.shader = cluster_render.cluster_render_shader.version_get_shader(cluster_render.shader_version, 0);
|
||||
cluster_render.shader_pipelines[ClusterRender::PIPELINE_NORMAL] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, fb_format, vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rasterization_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
|
||||
RD::PipelineMultisampleState ms;
|
||||
ms.sample_count = RD::TEXTURE_SAMPLES_4;
|
||||
cluster_render.shader_pipelines[ClusterRender::PIPELINE_MSAA] = RD::get_singleton()->render_pipeline_create(cluster_render.shader, fb_format, vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rasterization_state, ms, RD::PipelineDepthStencilState(), blend_state, 0);
|
||||
}
|
||||
{
|
||||
Vector<String> versions;
|
||||
versions.push_back("");
|
||||
cluster_store.cluster_store_shader.initialize(versions);
|
||||
cluster_store.shader_version = cluster_store.cluster_store_shader.version_create();
|
||||
cluster_store.shader = cluster_store.cluster_store_shader.version_get_shader(cluster_store.shader_version, 0);
|
||||
cluster_store.shader_pipeline = RD::get_singleton()->compute_pipeline_create(cluster_store.shader);
|
||||
}
|
||||
{
|
||||
Vector<String> versions;
|
||||
versions.push_back("");
|
||||
cluster_debug.cluster_debug_shader.initialize(versions);
|
||||
cluster_debug.shader_version = cluster_debug.cluster_debug_shader.version_create();
|
||||
cluster_debug.shader = cluster_debug.cluster_debug_shader.version_get_shader(cluster_debug.shader_version, 0);
|
||||
cluster_debug.shader_pipeline = RD::get_singleton()->compute_pipeline_create(cluster_debug.shader);
|
||||
}
|
||||
|
||||
{ // Sphere mesh data.
|
||||
static const uint32_t icosphere_vertex_count = 42;
|
||||
static const float icosphere_vertices[icosphere_vertex_count * 3] = {
|
||||
0, 0, -1, 0.7236073, -0.5257253, -0.4472195, -0.276388, -0.8506492, -0.4472199, -0.8944262, 0, -0.4472156, -0.276388, 0.8506492, -0.4472199, 0.7236073, 0.5257253, -0.4472195, 0.276388, -0.8506492, 0.4472199, -0.7236073, -0.5257253, 0.4472195, -0.7236073, 0.5257253, 0.4472195, 0.276388, 0.8506492, 0.4472199, 0.8944262, 0, 0.4472156, 0, 0, 1, -0.1624555, -0.4999952, -0.8506544, 0.4253227, -0.3090114, -0.8506542, 0.2628688, -0.8090116, -0.5257377, 0.8506479, 0, -0.5257359, 0.4253227, 0.3090114, -0.8506542, -0.5257298, 0, -0.8506517, -0.6881894, -0.4999969, -0.5257362, -0.1624555, 0.4999952, -0.8506544, -0.6881894, 0.4999969, -0.5257362, 0.2628688, 0.8090116, -0.5257377, 0.9510579, -0.3090126, 0, 0.9510579, 0.3090126, 0, 0, -1, 0, 0.5877856, -0.8090167, 0, -0.9510579, -0.3090126, 0, -0.5877856, -0.8090167, 0, -0.5877856, 0.8090167, 0, -0.9510579, 0.3090126, 0, 0.5877856, 0.8090167, 0, 0, 1, 0, 0.6881894, -0.4999969, 0.5257362, -0.2628688, -0.8090116, 0.5257377, -0.8506479, 0, 0.5257359, -0.2628688, 0.8090116, 0.5257377, 0.6881894, 0.4999969, 0.5257362, 0.1624555, -0.4999952, 0.8506544, 0.5257298, 0, 0.8506517, -0.4253227, -0.3090114, 0.8506542, -0.4253227, 0.3090114, 0.8506542, 0.1624555, 0.4999952, 0.8506544
|
||||
};
|
||||
static const uint32_t icosphere_triangle_count = 80;
|
||||
static const uint16_t icosphere_triangle_indices[icosphere_triangle_count * 3] = {
|
||||
0, 13, 12, 1, 13, 15, 0, 12, 17, 0, 17, 19, 0, 19, 16, 1, 15, 22, 2, 14, 24, 3, 18, 26, 4, 20, 28, 5, 21, 30, 1, 22, 25, 2, 24, 27, 3, 26, 29, 4, 28, 31, 5, 30, 23, 6, 32, 37, 7, 33, 39, 8, 34, 40, 9, 35, 41, 10, 36, 38, 38, 41, 11, 38, 36, 41, 36, 9, 41, 41, 40, 11, 41, 35, 40, 35, 8, 40, 40, 39, 11, 40, 34, 39, 34, 7, 39, 39, 37, 11, 39, 33, 37, 33, 6, 37, 37, 38, 11, 37, 32, 38, 32, 10, 38, 23, 36, 10, 23, 30, 36, 30, 9, 36, 31, 35, 9, 31, 28, 35, 28, 8, 35, 29, 34, 8, 29, 26, 34, 26, 7, 34, 27, 33, 7, 27, 24, 33, 24, 6, 33, 25, 32, 6, 25, 22, 32, 22, 10, 32, 30, 31, 9, 30, 21, 31, 21, 4, 31, 28, 29, 8, 28, 20, 29, 20, 3, 29, 26, 27, 7, 26, 18, 27, 18, 2, 27, 24, 25, 6, 24, 14, 25, 14, 1, 25, 22, 23, 10, 22, 15, 23, 15, 5, 23, 16, 21, 5, 16, 19, 21, 19, 4, 21, 19, 20, 4, 19, 17, 20, 17, 3, 20, 17, 18, 3, 17, 12, 18, 12, 2, 18, 15, 16, 5, 15, 13, 16, 13, 0, 16, 12, 14, 2, 12, 13, 14, 13, 1, 14
|
||||
};
|
||||
|
||||
Vector<uint8_t> vertex_data;
|
||||
vertex_data.resize(sizeof(float) * icosphere_vertex_count * 3);
|
||||
memcpy(vertex_data.ptrw(), icosphere_vertices, vertex_data.size());
|
||||
|
||||
sphere_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data);
|
||||
|
||||
Vector<uint8_t> index_data;
|
||||
index_data.resize(sizeof(uint16_t) * icosphere_triangle_count * 3);
|
||||
memcpy(index_data.ptrw(), icosphere_triangle_indices, index_data.size());
|
||||
|
||||
sphere_index_buffer = RD::get_singleton()->index_buffer_create(icosphere_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT16, index_data);
|
||||
|
||||
Vector<RID> buffers;
|
||||
buffers.push_back(sphere_vertex_buffer);
|
||||
|
||||
sphere_vertex_array = RD::get_singleton()->vertex_array_create(icosphere_vertex_count, vertex_format, buffers);
|
||||
|
||||
sphere_index_array = RD::get_singleton()->index_array_create(sphere_index_buffer, 0, icosphere_triangle_count * 3);
|
||||
|
||||
float min_d = 1e20;
|
||||
for (uint32_t i = 0; i < icosphere_triangle_count; i++) {
|
||||
Vector3 vertices[3];
|
||||
for (uint32_t j = 0; j < 3; j++) {
|
||||
uint32_t index = icosphere_triangle_indices[i * 3 + j];
|
||||
for (uint32_t k = 0; k < 3; k++) {
|
||||
vertices[j][k] = icosphere_vertices[index * 3 + k];
|
||||
}
|
||||
}
|
||||
Plane p(vertices[0], vertices[1], vertices[2]);
|
||||
min_d = MIN(Math::abs(p.d), min_d);
|
||||
}
|
||||
sphere_overfit = 1.0 / min_d;
|
||||
}
|
||||
|
||||
{ // Cone mesh data.
|
||||
static const uint32_t cone_vertex_count = 99;
|
||||
static const float cone_vertices[cone_vertex_count * 3] = {
|
||||
0, 1, -1, 0.1950903, 0.9807853, -1, 0.3826835, 0.9238795, -1, 0.5555703, 0.8314696, -1, 0.7071068, 0.7071068, -1, 0.8314697, 0.5555702, -1, 0.9238795, 0.3826834, -1, 0.9807853, 0.1950903, -1, 1, 0, -1, 0.9807853, -0.1950902, -1, 0.9238796, -0.3826833, -1, 0.8314697, -0.5555702, -1, 0.7071068, -0.7071068, -1, 0.5555702, -0.8314697, -1, 0.3826833, -0.9238796, -1, 0.1950901, -0.9807853, -1, -3.25841e-7, -1, -1, -0.1950907, -0.9807852, -1, -0.3826839, -0.9238793, -1, -0.5555707, -0.8314693, -1, -0.7071073, -0.7071063, -1, -0.83147, -0.5555697, -1, -0.9238799, -0.3826827, -1, 0, 0, 0, -0.9807854, -0.1950894, -1, -1, 9.65599e-7, -1, -0.9807851, 0.1950913, -1, -0.9238791, 0.3826845, -1, -0.8314689, 0.5555713, -1, -0.7071059, 0.7071077, -1, -0.5555691, 0.8314704, -1, -0.3826821, 0.9238801, -1, -0.1950888, 0.9807856, -1
|
||||
};
|
||||
static const uint32_t cone_triangle_count = 62;
|
||||
static const uint16_t cone_triangle_indices[cone_triangle_count * 3] = {
|
||||
0, 23, 1, 1, 23, 2, 2, 23, 3, 3, 23, 4, 4, 23, 5, 5, 23, 6, 6, 23, 7, 7, 23, 8, 8, 23, 9, 9, 23, 10, 10, 23, 11, 11, 23, 12, 12, 23, 13, 13, 23, 14, 14, 23, 15, 15, 23, 16, 16, 23, 17, 17, 23, 18, 18, 23, 19, 19, 23, 20, 20, 23, 21, 21, 23, 22, 22, 23, 24, 24, 23, 25, 25, 23, 26, 26, 23, 27, 27, 23, 28, 28, 23, 29, 29, 23, 30, 30, 23, 31, 31, 23, 32, 32, 23, 0, 7, 15, 24, 32, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 3, 6, 7, 3, 7, 8, 9, 9, 10, 7, 10, 11, 7, 11, 12, 15, 12, 13, 15, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20, 24, 20, 21, 24, 21, 22, 24, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 1, 3, 15, 17, 24, 17, 19, 24, 24, 26, 32, 26, 28, 32, 28, 30, 32, 32, 3, 7, 7, 11, 15, 32, 7, 24
|
||||
};
|
||||
|
||||
Vector<uint8_t> vertex_data;
|
||||
vertex_data.resize(sizeof(float) * cone_vertex_count * 3);
|
||||
memcpy(vertex_data.ptrw(), cone_vertices, vertex_data.size());
|
||||
|
||||
cone_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data);
|
||||
|
||||
Vector<uint8_t> index_data;
|
||||
index_data.resize(sizeof(uint16_t) * cone_triangle_count * 3);
|
||||
memcpy(index_data.ptrw(), cone_triangle_indices, index_data.size());
|
||||
|
||||
cone_index_buffer = RD::get_singleton()->index_buffer_create(cone_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT16, index_data);
|
||||
|
||||
Vector<RID> buffers;
|
||||
buffers.push_back(cone_vertex_buffer);
|
||||
|
||||
cone_vertex_array = RD::get_singleton()->vertex_array_create(cone_vertex_count, vertex_format, buffers);
|
||||
|
||||
cone_index_array = RD::get_singleton()->index_array_create(cone_index_buffer, 0, cone_triangle_count * 3);
|
||||
|
||||
float min_d = 1e20;
|
||||
for (uint32_t i = 0; i < cone_triangle_count; i++) {
|
||||
Vector3 vertices[3];
|
||||
int32_t zero_index = -1;
|
||||
for (uint32_t j = 0; j < 3; j++) {
|
||||
uint32_t index = cone_triangle_indices[i * 3 + j];
|
||||
for (uint32_t k = 0; k < 3; k++) {
|
||||
vertices[j][k] = cone_vertices[index * 3 + k];
|
||||
}
|
||||
if (vertices[j] == Vector3()) {
|
||||
zero_index = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (zero_index != -1) {
|
||||
Vector3 a = vertices[(zero_index + 1) % 3];
|
||||
Vector3 b = vertices[(zero_index + 2) % 3];
|
||||
Vector3 c = a + Vector3(0, 0, 1);
|
||||
Plane p(a, b, c);
|
||||
min_d = MIN(Math::abs(p.d), min_d);
|
||||
}
|
||||
}
|
||||
cone_overfit = 1.0 / min_d;
|
||||
}
|
||||
|
||||
{ // Box mesh data.
|
||||
static const uint32_t box_vertex_count = 8;
|
||||
static const float box_vertices[box_vertex_count * 3] = {
|
||||
-1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1
|
||||
};
|
||||
static const uint32_t box_triangle_count = 12;
|
||||
static const uint16_t box_triangle_indices[box_triangle_count * 3] = {
|
||||
1, 2, 0, 3, 6, 2, 7, 4, 6, 5, 0, 4, 6, 0, 2, 3, 5, 7, 1, 3, 2, 3, 7, 6, 7, 5, 4, 5, 1, 0, 6, 4, 0, 3, 1, 5
|
||||
};
|
||||
|
||||
Vector<uint8_t> vertex_data;
|
||||
vertex_data.resize(sizeof(float) * box_vertex_count * 3);
|
||||
memcpy(vertex_data.ptrw(), box_vertices, vertex_data.size());
|
||||
|
||||
box_vertex_buffer = RD::get_singleton()->vertex_buffer_create(vertex_data.size(), vertex_data);
|
||||
|
||||
Vector<uint8_t> index_data;
|
||||
index_data.resize(sizeof(uint16_t) * box_triangle_count * 3);
|
||||
memcpy(index_data.ptrw(), box_triangle_indices, index_data.size());
|
||||
|
||||
box_index_buffer = RD::get_singleton()->index_buffer_create(box_triangle_count * 3, RD::INDEX_BUFFER_FORMAT_UINT16, index_data);
|
||||
|
||||
Vector<RID> buffers;
|
||||
buffers.push_back(box_vertex_buffer);
|
||||
|
||||
box_vertex_array = RD::get_singleton()->vertex_array_create(box_vertex_count, vertex_format, buffers);
|
||||
|
||||
box_index_array = RD::get_singleton()->index_array_create(box_index_buffer, 0, box_triangle_count * 3);
|
||||
}
|
||||
}
|
||||
ClusterBuilderSharedDataRD::~ClusterBuilderSharedDataRD() {
|
||||
RD::get_singleton()->free(sphere_vertex_buffer);
|
||||
RD::get_singleton()->free(sphere_index_buffer);
|
||||
RD::get_singleton()->free(cone_vertex_buffer);
|
||||
RD::get_singleton()->free(cone_index_buffer);
|
||||
RD::get_singleton()->free(box_vertex_buffer);
|
||||
RD::get_singleton()->free(box_index_buffer);
|
||||
|
||||
cluster_render.cluster_render_shader.version_free(cluster_render.shader_version);
|
||||
cluster_store.cluster_store_shader.version_free(cluster_store.shader_version);
|
||||
cluster_debug.cluster_debug_shader.version_free(cluster_debug.shader_version);
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void ClusterBuilderRD::_clear() {
|
||||
if (cluster_buffer.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RD::get_singleton()->free(cluster_buffer);
|
||||
RD::get_singleton()->free(cluster_render_buffer);
|
||||
RD::get_singleton()->free(element_buffer);
|
||||
cluster_buffer = RID();
|
||||
cluster_render_buffer = RID();
|
||||
element_buffer = RID();
|
||||
|
||||
memfree(render_elements);
|
||||
|
||||
render_elements = nullptr;
|
||||
render_element_max = 0;
|
||||
render_element_count = 0;
|
||||
|
||||
RD::get_singleton()->free(framebuffer);
|
||||
framebuffer = RID();
|
||||
|
||||
cluster_render_uniform_set = RID();
|
||||
cluster_store_uniform_set = RID();
|
||||
}
|
||||
|
||||
void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID p_depth_buffer, RID p_depth_buffer_sampler, RID p_color_buffer) {
|
||||
ERR_FAIL_COND(p_max_elements == 0);
|
||||
ERR_FAIL_COND(p_screen_size.x < 1);
|
||||
ERR_FAIL_COND(p_screen_size.y < 1);
|
||||
|
||||
_clear();
|
||||
|
||||
screen_size = p_screen_size;
|
||||
|
||||
cluster_screen_size.width = Math::division_round_up((uint32_t)p_screen_size.width, cluster_size);
|
||||
cluster_screen_size.height = Math::division_round_up((uint32_t)p_screen_size.height, cluster_size);
|
||||
|
||||
max_elements_by_type = p_max_elements;
|
||||
if (max_elements_by_type % 32) { // Needs to be aligned to 32.
|
||||
max_elements_by_type += 32 - (max_elements_by_type % 32);
|
||||
}
|
||||
|
||||
cluster_buffer_size = cluster_screen_size.x * cluster_screen_size.y * (max_elements_by_type / 32 + 32) * ELEMENT_TYPE_MAX * 4;
|
||||
|
||||
render_element_max = max_elements_by_type * ELEMENT_TYPE_MAX;
|
||||
|
||||
uint32_t element_tag_bits_size = render_element_max / 32;
|
||||
uint32_t element_tag_depth_bits_size = render_element_max;
|
||||
|
||||
cluster_render_buffer_size = cluster_screen_size.x * cluster_screen_size.y * (element_tag_bits_size + element_tag_depth_bits_size) * 4; // Tag bits (element was used) and tag depth (depth range in which it was used).
|
||||
|
||||
cluster_render_buffer = RD::get_singleton()->storage_buffer_create(cluster_render_buffer_size);
|
||||
cluster_buffer = RD::get_singleton()->storage_buffer_create(cluster_buffer_size);
|
||||
|
||||
render_elements = static_cast<RenderElementData *>(memalloc(sizeof(RenderElementData) * render_element_max));
|
||||
render_element_count = 0;
|
||||
|
||||
element_buffer = RD::get_singleton()->storage_buffer_create(sizeof(RenderElementData) * render_element_max);
|
||||
|
||||
uint32_t div_value = 1 << divisor;
|
||||
if (use_msaa) {
|
||||
framebuffer = RD::get_singleton()->framebuffer_create_empty(p_screen_size / div_value, RD::TEXTURE_SAMPLES_4);
|
||||
} else {
|
||||
framebuffer = RD::get_singleton()->framebuffer_create_empty(p_screen_size / div_value);
|
||||
}
|
||||
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
|
||||
u.binding = 1;
|
||||
u.append_id(state_uniform);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 2;
|
||||
u.append_id(element_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 3;
|
||||
u.append_id(cluster_render_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
cluster_render_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shared->cluster_render.shader, 0);
|
||||
}
|
||||
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 1;
|
||||
u.append_id(cluster_render_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 2;
|
||||
u.append_id(cluster_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 3;
|
||||
u.append_id(element_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
cluster_store_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shared->cluster_store.shader, 0);
|
||||
}
|
||||
|
||||
if (p_color_buffer.is_valid()) {
|
||||
Vector<RD::Uniform> uniforms;
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.binding = 1;
|
||||
u.append_id(cluster_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
|
||||
u.binding = 2;
|
||||
u.append_id(p_color_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
|
||||
u.binding = 3;
|
||||
u.append_id(p_depth_buffer);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
{
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
|
||||
u.binding = 4;
|
||||
u.append_id(p_depth_buffer_sampler);
|
||||
uniforms.push_back(u);
|
||||
}
|
||||
|
||||
debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shared->cluster_debug.shader, 0);
|
||||
} else {
|
||||
debug_uniform_set = RID();
|
||||
}
|
||||
}
|
||||
|
||||
void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y) {
|
||||
view_xform = p_view_transform.affine_inverse();
|
||||
projection = p_cam_projection;
|
||||
z_near = projection.get_z_near();
|
||||
z_far = projection.get_z_far();
|
||||
camera_orthogonal = p_cam_projection.is_orthogonal();
|
||||
adjusted_projection = projection;
|
||||
if (!camera_orthogonal) {
|
||||
adjusted_projection.adjust_perspective_znear(0.0001);
|
||||
}
|
||||
|
||||
Projection correction;
|
||||
correction.set_depth_correction(p_flip_y);
|
||||
projection = correction * projection;
|
||||
adjusted_projection = correction * adjusted_projection;
|
||||
|
||||
// Reset counts.
|
||||
render_element_count = 0;
|
||||
for (uint32_t i = 0; i < ELEMENT_TYPE_MAX; i++) {
|
||||
cluster_count_by_type[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ClusterBuilderRD::bake_cluster() {
|
||||
RENDER_TIMESTAMP("> Bake 3D Cluster");
|
||||
|
||||
RD::get_singleton()->draw_command_begin_label("Bake Light Cluster");
|
||||
|
||||
// Clear cluster buffer.
|
||||
RD::get_singleton()->buffer_clear(cluster_buffer, 0, cluster_buffer_size);
|
||||
|
||||
if (render_element_count > 0) {
|
||||
// Clear render buffer.
|
||||
RD::get_singleton()->buffer_clear(cluster_render_buffer, 0, cluster_render_buffer_size);
|
||||
|
||||
{ // Fill state uniform.
|
||||
|
||||
StateUniform state;
|
||||
|
||||
RendererRD::MaterialStorage::store_camera(adjusted_projection, state.projection);
|
||||
state.inv_z_far = 1.0 / z_far;
|
||||
state.screen_to_clusters_shift = get_shift_from_power_of_2(cluster_size);
|
||||
state.screen_to_clusters_shift -= divisor; //screen is smaller, shift one less
|
||||
|
||||
state.cluster_screen_width = cluster_screen_size.x;
|
||||
state.cluster_depth_offset = (render_element_max / 32);
|
||||
state.cluster_data_size = state.cluster_depth_offset + render_element_max;
|
||||
|
||||
RD::get_singleton()->buffer_update(state_uniform, 0, sizeof(StateUniform), &state);
|
||||
}
|
||||
|
||||
// Update instances.
|
||||
|
||||
RD::get_singleton()->buffer_update(element_buffer, 0, sizeof(RenderElementData) * render_element_count, render_elements);
|
||||
|
||||
RENDER_TIMESTAMP("Render 3D Cluster Elements");
|
||||
|
||||
// Render elements.
|
||||
{
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
|
||||
ClusterBuilderSharedDataRD::ClusterRender::PushConstant push_constant = {};
|
||||
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shared->cluster_render.shader_pipelines[use_msaa ? ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_MSAA : ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_NORMAL]);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, cluster_render_uniform_set, 0);
|
||||
|
||||
for (uint32_t i = 0; i < render_element_count;) {
|
||||
push_constant.base_index = i;
|
||||
switch (render_elements[i].type) {
|
||||
case ELEMENT_TYPE_OMNI_LIGHT: {
|
||||
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, shared->sphere_vertex_array);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, shared->sphere_index_array);
|
||||
} break;
|
||||
case ELEMENT_TYPE_SPOT_LIGHT: {
|
||||
// If the spot angle is above a certain threshold, use a sphere instead of a cone for building the clusters
|
||||
// since the cone gets too flat/large (spot angle close to 90 degrees) or
|
||||
// can't even cover the affected area of the light (spot angle above 90 degrees).
|
||||
if (render_elements[i].has_wide_spot_angle) {
|
||||
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, shared->sphere_vertex_array);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, shared->sphere_index_array);
|
||||
} else {
|
||||
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, shared->cone_vertex_array);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, shared->cone_index_array);
|
||||
}
|
||||
} break;
|
||||
case ELEMENT_TYPE_DECAL:
|
||||
case ELEMENT_TYPE_REFLECTION_PROBE: {
|
||||
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, shared->box_vertex_array);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, shared->box_index_array);
|
||||
} break;
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ClusterBuilderSharedDataRD::ClusterRender::PushConstant));
|
||||
|
||||
uint32_t instances = 1;
|
||||
RD::get_singleton()->draw_list_draw(draw_list, true, instances);
|
||||
i += instances;
|
||||
}
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
// Store elements.
|
||||
RENDER_TIMESTAMP("Pack 3D Cluster Elements");
|
||||
|
||||
{
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shared->cluster_store.shader_pipeline);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cluster_store_uniform_set, 0);
|
||||
|
||||
ClusterBuilderSharedDataRD::ClusterStore::PushConstant push_constant;
|
||||
push_constant.cluster_render_data_size = render_element_max / 32 + render_element_max;
|
||||
push_constant.max_render_element_count_div_32 = render_element_max / 32;
|
||||
push_constant.cluster_screen_size[0] = cluster_screen_size.x;
|
||||
push_constant.cluster_screen_size[1] = cluster_screen_size.y;
|
||||
|
||||
push_constant.render_element_count_div_32 = Math::division_round_up(render_element_count, 32U);
|
||||
push_constant.max_cluster_element_count_div_32 = max_elements_by_type / 32;
|
||||
push_constant.pad1 = 0;
|
||||
push_constant.pad2 = 0;
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ClusterBuilderSharedDataRD::ClusterStore::PushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, cluster_screen_size.x, cluster_screen_size.y, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
}
|
||||
RENDER_TIMESTAMP("< Bake 3D Cluster");
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
|
||||
void ClusterBuilderRD::debug(ElementType p_element) {
|
||||
ERR_FAIL_COND(debug_uniform_set.is_null());
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shared->cluster_debug.shader_pipeline);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, debug_uniform_set, 0);
|
||||
|
||||
ClusterBuilderSharedDataRD::ClusterDebug::PushConstant push_constant;
|
||||
push_constant.screen_size[0] = screen_size.x;
|
||||
push_constant.screen_size[1] = screen_size.y;
|
||||
push_constant.cluster_screen_size[0] = cluster_screen_size.x;
|
||||
push_constant.cluster_screen_size[1] = cluster_screen_size.y;
|
||||
push_constant.cluster_shift = get_shift_from_power_of_2(cluster_size);
|
||||
push_constant.cluster_type = p_element;
|
||||
push_constant.orthogonal = camera_orthogonal;
|
||||
push_constant.z_far = z_far;
|
||||
push_constant.z_near = z_near;
|
||||
push_constant.max_cluster_element_count_div_32 = max_elements_by_type / 32;
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ClusterBuilderSharedDataRD::ClusterDebug::PushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, screen_size.x, screen_size.y, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
RID ClusterBuilderRD::get_cluster_buffer() const {
|
||||
return cluster_buffer;
|
||||
}
|
||||
|
||||
uint32_t ClusterBuilderRD::get_cluster_size() const {
|
||||
return cluster_size;
|
||||
}
|
||||
|
||||
uint32_t ClusterBuilderRD::get_max_cluster_elements() const {
|
||||
return max_elements_by_type;
|
||||
}
|
||||
|
||||
void ClusterBuilderRD::set_shared(ClusterBuilderSharedDataRD *p_shared) {
|
||||
shared = p_shared;
|
||||
}
|
||||
|
||||
ClusterBuilderRD::ClusterBuilderRD() {
|
||||
state_uniform = RD::get_singleton()->uniform_buffer_create(sizeof(StateUniform));
|
||||
}
|
||||
|
||||
ClusterBuilderRD::~ClusterBuilderRD() {
|
||||
_clear();
|
||||
RD::get_singleton()->free(state_uniform);
|
||||
}
|
||||
391
engine/servers/rendering/renderer_rd/cluster_builder_rd.h
Normal file
391
engine/servers/rendering/renderer_rd/cluster_builder_rd.h
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/**************************************************************************/
|
||||
/* cluster_builder_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CLUSTER_BUILDER_RD_H
|
||||
#define CLUSTER_BUILDER_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/shaders/cluster_debug.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/cluster_render.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/cluster_store.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
|
||||
class ClusterBuilderSharedDataRD {
|
||||
friend class ClusterBuilderRD;
|
||||
|
||||
RID sphere_vertex_buffer;
|
||||
RID sphere_vertex_array;
|
||||
RID sphere_index_buffer;
|
||||
RID sphere_index_array;
|
||||
float sphere_overfit = 0.0; // Because an icosphere is not a perfect sphere, we need to enlarge it to cover the sphere area.
|
||||
|
||||
RID cone_vertex_buffer;
|
||||
RID cone_vertex_array;
|
||||
RID cone_index_buffer;
|
||||
RID cone_index_array;
|
||||
float cone_overfit = 0.0; // Because an cone mesh is not a perfect cone, we need to enlarge it to cover the actual cone area.
|
||||
|
||||
RID box_vertex_buffer;
|
||||
RID box_vertex_array;
|
||||
RID box_index_buffer;
|
||||
RID box_index_array;
|
||||
|
||||
enum Divisor {
|
||||
DIVISOR_1,
|
||||
DIVISOR_2,
|
||||
DIVISOR_4,
|
||||
};
|
||||
|
||||
struct ClusterRender {
|
||||
struct PushConstant {
|
||||
uint32_t base_index;
|
||||
uint32_t pad0;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
ClusterRenderShaderRD cluster_render_shader;
|
||||
RID shader_version;
|
||||
RID shader;
|
||||
|
||||
enum PipelineVersion {
|
||||
PIPELINE_NORMAL,
|
||||
PIPELINE_MSAA,
|
||||
PIPELINE_MAX
|
||||
};
|
||||
|
||||
RID shader_pipelines[PIPELINE_MAX];
|
||||
} cluster_render;
|
||||
|
||||
struct ClusterStore {
|
||||
struct PushConstant {
|
||||
uint32_t cluster_render_data_size; // how much data for a single cluster takes
|
||||
uint32_t max_render_element_count_div_32; // divided by 32
|
||||
uint32_t cluster_screen_size[2];
|
||||
uint32_t render_element_count_div_32; // divided by 32
|
||||
uint32_t max_cluster_element_count_div_32; // divided by 32
|
||||
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
ClusterStoreShaderRD cluster_store_shader;
|
||||
RID shader_version;
|
||||
RID shader;
|
||||
RID shader_pipeline;
|
||||
} cluster_store;
|
||||
|
||||
struct ClusterDebug {
|
||||
struct PushConstant {
|
||||
uint32_t screen_size[2];
|
||||
uint32_t cluster_screen_size[2];
|
||||
|
||||
uint32_t cluster_shift;
|
||||
uint32_t cluster_type;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
uint32_t orthogonal;
|
||||
uint32_t max_cluster_element_count_div_32;
|
||||
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
ClusterDebugShaderRD cluster_debug_shader;
|
||||
RID shader_version;
|
||||
RID shader;
|
||||
RID shader_pipeline;
|
||||
} cluster_debug;
|
||||
|
||||
public:
|
||||
ClusterBuilderSharedDataRD();
|
||||
~ClusterBuilderSharedDataRD();
|
||||
};
|
||||
|
||||
class ClusterBuilderRD {
|
||||
public:
|
||||
static constexpr float WIDE_SPOT_ANGLE_THRESHOLD_DEG = 60.0f;
|
||||
|
||||
enum LightType {
|
||||
LIGHT_TYPE_OMNI,
|
||||
LIGHT_TYPE_SPOT
|
||||
};
|
||||
|
||||
enum BoxType {
|
||||
BOX_TYPE_REFLECTION_PROBE,
|
||||
BOX_TYPE_DECAL,
|
||||
};
|
||||
|
||||
enum ElementType {
|
||||
ELEMENT_TYPE_OMNI_LIGHT,
|
||||
ELEMENT_TYPE_SPOT_LIGHT,
|
||||
ELEMENT_TYPE_DECAL,
|
||||
ELEMENT_TYPE_REFLECTION_PROBE,
|
||||
ELEMENT_TYPE_MAX,
|
||||
};
|
||||
|
||||
private:
|
||||
ClusterBuilderSharedDataRD *shared = nullptr;
|
||||
|
||||
struct RenderElementData {
|
||||
uint32_t type; // 0-4
|
||||
uint32_t touches_near;
|
||||
uint32_t touches_far;
|
||||
uint32_t original_index;
|
||||
float transform_inv[12]; // Transposed transform for less space.
|
||||
float scale[3];
|
||||
uint32_t has_wide_spot_angle;
|
||||
}; // Keep aligned to 32 bytes.
|
||||
|
||||
uint32_t cluster_count_by_type[ELEMENT_TYPE_MAX] = {};
|
||||
uint32_t max_elements_by_type = 0;
|
||||
|
||||
RenderElementData *render_elements = nullptr;
|
||||
uint32_t render_element_count = 0;
|
||||
uint32_t render_element_max = 0;
|
||||
|
||||
Transform3D view_xform;
|
||||
Projection adjusted_projection;
|
||||
Projection projection;
|
||||
float z_far = 0;
|
||||
float z_near = 0;
|
||||
bool camera_orthogonal = false;
|
||||
|
||||
enum Divisor {
|
||||
DIVISOR_1,
|
||||
DIVISOR_2,
|
||||
DIVISOR_4,
|
||||
};
|
||||
|
||||
uint32_t cluster_size = 32;
|
||||
bool use_msaa = true;
|
||||
Divisor divisor = DIVISOR_4;
|
||||
|
||||
Size2i screen_size;
|
||||
Size2i cluster_screen_size;
|
||||
|
||||
RID framebuffer;
|
||||
RID cluster_render_buffer; // Used for creating.
|
||||
RID cluster_buffer; // Used for rendering.
|
||||
RID element_buffer; // Used for storing, to hint element touches far plane or near plane.
|
||||
uint32_t cluster_render_buffer_size = 0;
|
||||
uint32_t cluster_buffer_size = 0;
|
||||
|
||||
RID cluster_render_uniform_set;
|
||||
RID cluster_store_uniform_set;
|
||||
|
||||
// Persistent data.
|
||||
|
||||
void _clear();
|
||||
|
||||
struct StateUniform {
|
||||
float projection[16];
|
||||
float inv_z_far;
|
||||
uint32_t screen_to_clusters_shift; // Shift to obtain coordinates in block indices.
|
||||
uint32_t cluster_screen_width;
|
||||
uint32_t cluster_data_size; // How much data is needed for a single cluster.
|
||||
uint32_t cluster_depth_offset;
|
||||
|
||||
uint32_t pad0;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
RID state_uniform;
|
||||
|
||||
RID debug_uniform_set;
|
||||
|
||||
public:
|
||||
void setup(Size2i p_screen_size, uint32_t p_max_elements, RID p_depth_buffer, RID p_depth_buffer_sampler, RID p_color_buffer);
|
||||
|
||||
void begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y);
|
||||
|
||||
_FORCE_INLINE_ void add_light(LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture) {
|
||||
if (p_type == LIGHT_TYPE_OMNI && cluster_count_by_type[ELEMENT_TYPE_OMNI_LIGHT] == max_elements_by_type) {
|
||||
return; // Max number elements reached.
|
||||
}
|
||||
if (p_type == LIGHT_TYPE_SPOT && cluster_count_by_type[ELEMENT_TYPE_SPOT_LIGHT] == max_elements_by_type) {
|
||||
return; // Max number elements reached.
|
||||
}
|
||||
|
||||
RenderElementData &e = render_elements[render_element_count];
|
||||
|
||||
Transform3D xform = view_xform * p_transform;
|
||||
|
||||
float radius = xform.basis.get_uniform_scale();
|
||||
if (radius < 0.98 || radius > 1.02) {
|
||||
xform.basis.orthonormalize();
|
||||
}
|
||||
|
||||
radius *= p_radius;
|
||||
|
||||
if (p_type == LIGHT_TYPE_OMNI) {
|
||||
radius *= shared->sphere_overfit; // Overfit icosphere.
|
||||
|
||||
float depth = -xform.origin.z;
|
||||
if (camera_orthogonal) {
|
||||
e.touches_near = (depth - radius) < z_near;
|
||||
} else {
|
||||
// Contains camera inside light.
|
||||
float radius2 = radius * shared->sphere_overfit; // Overfit again for outer size (camera may be outside actual sphere but behind an icosphere vertex)
|
||||
e.touches_near = xform.origin.length_squared() < radius2 * radius2;
|
||||
}
|
||||
|
||||
e.touches_far = (depth + radius) > z_far;
|
||||
e.scale[0] = radius;
|
||||
e.scale[1] = radius;
|
||||
e.scale[2] = radius;
|
||||
e.type = ELEMENT_TYPE_OMNI_LIGHT;
|
||||
e.original_index = cluster_count_by_type[ELEMENT_TYPE_OMNI_LIGHT];
|
||||
|
||||
RendererRD::MaterialStorage::store_transform_transposed_3x4(xform, e.transform_inv);
|
||||
|
||||
cluster_count_by_type[ELEMENT_TYPE_OMNI_LIGHT]++;
|
||||
|
||||
} else /*LIGHT_TYPE_SPOT */ {
|
||||
radius *= shared->cone_overfit; // Overfit icosphere
|
||||
|
||||
real_t len = Math::tan(Math::deg_to_rad(p_spot_aperture)) * radius;
|
||||
// Approximate, probably better to use a cone support function.
|
||||
float max_d = -1e20;
|
||||
float min_d = 1e20;
|
||||
#define CONE_MINMAX(m_x, m_y) \
|
||||
{ \
|
||||
float d = -xform.xform(Vector3(len * m_x, len * m_y, -radius)).z; \
|
||||
min_d = MIN(d, min_d); \
|
||||
max_d = MAX(d, max_d); \
|
||||
}
|
||||
|
||||
CONE_MINMAX(1, 1);
|
||||
CONE_MINMAX(-1, 1);
|
||||
CONE_MINMAX(-1, -1);
|
||||
CONE_MINMAX(1, -1);
|
||||
|
||||
if (camera_orthogonal) {
|
||||
e.touches_near = min_d < z_near;
|
||||
} else {
|
||||
Plane base_plane(-xform.basis.get_column(Vector3::AXIS_Z), xform.origin);
|
||||
float dist = base_plane.distance_to(Vector3());
|
||||
if (dist >= 0 && dist < radius) {
|
||||
// Contains camera inside light, check angle.
|
||||
float angle = Math::rad_to_deg(Math::acos((-xform.origin.normalized()).dot(-xform.basis.get_column(Vector3::AXIS_Z))));
|
||||
e.touches_near = angle < p_spot_aperture * 1.05; //overfit aperture a little due to cone overfit
|
||||
} else {
|
||||
e.touches_near = false;
|
||||
}
|
||||
}
|
||||
|
||||
e.touches_far = max_d > z_far;
|
||||
|
||||
// If the spot angle is above the threshold, use a sphere instead of a cone for building the clusters
|
||||
// since the cone gets too flat/large (spot angle close to 90 degrees) or
|
||||
// can't even cover the affected area of the light (spot angle above 90 degrees).
|
||||
if (p_spot_aperture > WIDE_SPOT_ANGLE_THRESHOLD_DEG) {
|
||||
e.scale[0] = radius;
|
||||
e.scale[1] = radius;
|
||||
e.scale[2] = radius;
|
||||
e.has_wide_spot_angle = true;
|
||||
} else {
|
||||
e.scale[0] = len * shared->cone_overfit;
|
||||
e.scale[1] = len * shared->cone_overfit;
|
||||
e.scale[2] = radius;
|
||||
e.has_wide_spot_angle = false;
|
||||
}
|
||||
|
||||
e.type = ELEMENT_TYPE_SPOT_LIGHT;
|
||||
e.original_index = cluster_count_by_type[ELEMENT_TYPE_SPOT_LIGHT]; // Use omni light since they share index.
|
||||
|
||||
RendererRD::MaterialStorage::store_transform_transposed_3x4(xform, e.transform_inv);
|
||||
|
||||
cluster_count_by_type[ELEMENT_TYPE_SPOT_LIGHT]++;
|
||||
}
|
||||
|
||||
render_element_count++;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_box(BoxType p_box_type, const Transform3D &p_transform, const Vector3 &p_half_size) {
|
||||
if (p_box_type == BOX_TYPE_DECAL && cluster_count_by_type[ELEMENT_TYPE_DECAL] == max_elements_by_type) {
|
||||
return; // Max number elements reached.
|
||||
}
|
||||
if (p_box_type == BOX_TYPE_REFLECTION_PROBE && cluster_count_by_type[ELEMENT_TYPE_REFLECTION_PROBE] == max_elements_by_type) {
|
||||
return; // Max number elements reached.
|
||||
}
|
||||
|
||||
RenderElementData &e = render_elements[render_element_count];
|
||||
Transform3D xform = view_xform * p_transform;
|
||||
|
||||
// Extract scale and scale the matrix by it, makes things simpler.
|
||||
Vector3 scale = p_half_size;
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
float s = xform.basis.rows[i].length();
|
||||
scale[i] *= s;
|
||||
xform.basis.rows[i] /= s;
|
||||
};
|
||||
|
||||
float box_depth = Math::abs(xform.basis.xform_inv(Vector3(0, 0, -1)).dot(scale));
|
||||
float depth = -xform.origin.z;
|
||||
|
||||
if (camera_orthogonal) {
|
||||
e.touches_near = depth - box_depth < z_near;
|
||||
} else {
|
||||
// Contains camera inside box.
|
||||
Vector3 inside = xform.xform_inv(Vector3(0, 0, 0)).abs();
|
||||
e.touches_near = inside.x < scale.x && inside.y < scale.y && inside.z < scale.z;
|
||||
}
|
||||
|
||||
e.touches_far = depth + box_depth > z_far;
|
||||
|
||||
e.scale[0] = scale.x;
|
||||
e.scale[1] = scale.y;
|
||||
e.scale[2] = scale.z;
|
||||
|
||||
e.type = (p_box_type == BOX_TYPE_DECAL) ? ELEMENT_TYPE_DECAL : ELEMENT_TYPE_REFLECTION_PROBE;
|
||||
e.original_index = cluster_count_by_type[e.type];
|
||||
|
||||
RendererRD::MaterialStorage::store_transform_transposed_3x4(xform, e.transform_inv);
|
||||
|
||||
cluster_count_by_type[e.type]++;
|
||||
render_element_count++;
|
||||
}
|
||||
|
||||
void bake_cluster();
|
||||
void debug(ElementType p_element);
|
||||
|
||||
RID get_cluster_buffer() const;
|
||||
uint32_t get_cluster_size() const;
|
||||
uint32_t get_max_cluster_elements() const;
|
||||
|
||||
void set_shared(ClusterBuilderSharedDataRD *p_shared);
|
||||
|
||||
ClusterBuilderRD();
|
||||
~ClusterBuilderRD();
|
||||
};
|
||||
|
||||
#endif // CLUSTER_BUILDER_RD_H
|
||||
34
engine/servers/rendering/renderer_rd/effects/SCsub
Normal file
34
engine/servers/rendering/renderer_rd/effects/SCsub
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
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(CPPPATH=[thirdparty_dir])
|
||||
|
||||
# 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")
|
||||
env.servers_sources += module_obj
|
||||
|
||||
# Needed to force rebuilding the module files when the thirdparty library is updated.
|
||||
env.Depends(module_obj, thirdparty_obj)
|
||||
499
engine/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
Normal file
499
engine/servers/rendering/renderer_rd/effects/bokeh_dof.cpp
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
/**************************************************************************/
|
||||
/* 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/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
|
||||
#include "servers/rendering/rendering_server_default.h"
|
||||
#include "servers/rendering/storage/camera_attributes_storage.h"
|
||||
|
||||
using namespace RendererRD;
|
||||
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
engine/servers/rendering/renderer_rd/effects/bokeh_dof.h
Normal file
125
engine/servers/rendering/renderer_rd/effects/bokeh_dof.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BOKEH_DOF_RD_H
|
||||
#define BOKEH_DOF_RD_H
|
||||
|
||||
#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"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // BOKEH_DOF_RD_H
|
||||
1307
engine/servers/rendering/renderer_rd/effects/copy_effects.cpp
Normal file
1307
engine/servers/rendering/renderer_rd/effects/copy_effects.cpp
Normal file
File diff suppressed because it is too large
Load diff
363
engine/servers/rendering/renderer_rd/effects/copy_effects.h
Normal file
363
engine/servers/rendering/renderer_rd/effects/copy_effects.h
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COPY_EFFECTS_RD_H
|
||||
#define COPY_EFFECTS_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl.gen.h"
|
||||
#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];
|
||||
float screen_rect[4];
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#endif // COPY_EFFECTS_RD_H
|
||||
379
engine/servers/rendering/renderer_rd/effects/debug_effects.cpp
Normal file
379
engine/servers/rendering/renderer_rd/effects/debug_effects.cpp
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/**************************************************************************/
|
||||
/* 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/renderer_compositor_rd.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>(), false);
|
||||
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
|
||||
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();
|
||||
}
|
||||
101
engine/servers/rendering/renderer_rd/effects/debug_effects.h
Normal file
101
engine/servers/rendering/renderer_rd/effects/debug_effects.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DEBUG_EFFECTS_RD_H
|
||||
#define DEBUG_EFFECTS_RD_H
|
||||
|
||||
#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"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // DEBUG_EFFECTS_RD_H
|
||||
128
engine/servers/rendering/renderer_rd/effects/fsr.cpp
Normal file
128
engine/servers/rendering/renderer_rd/effects/fsr.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
|
||||
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
|
||||
// MoltenVK does not support some of the operations used by the normal mode of FSR. Fallback works just fine though.
|
||||
FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n");
|
||||
#else
|
||||
// Everyone else can use normal mode when available.
|
||||
if (RD::get_singleton()->has_feature(RD::SUPPORTS_FSR_HALF_FLOAT)) {
|
||||
FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_NORMAL\n");
|
||||
} else {
|
||||
FSR_upscale_modes.push_back("\n#define MODE_FSR_UPSCALE_FALLBACK\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
fsr_shader.initialize(FSR_upscale_modes);
|
||||
|
||||
shader_version = fsr_shader.version_create();
|
||||
pipeline = RD::get_singleton()->compute_pipeline_create(fsr_shader.version_get_shader(shader_version, 0));
|
||||
}
|
||||
|
||||
FSR::~FSR() {
|
||||
fsr_shader.version_free(shader_version);
|
||||
}
|
||||
|
||||
void FSR::fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) {
|
||||
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
|
||||
ERR_FAIL_NULL(uniform_set_cache);
|
||||
MaterialStorage *material_storage = MaterialStorage::get_singleton();
|
||||
ERR_FAIL_NULL(material_storage);
|
||||
|
||||
Size2i internal_size = p_render_buffers->get_internal_size();
|
||||
Size2i target_size = p_render_buffers->get_target_size();
|
||||
float fsr_upscale_sharpness = p_render_buffers->get_fsr_sharpness();
|
||||
|
||||
if (!p_render_buffers->has_texture(SNAME("FSR"), SNAME("upscale_texture"))) {
|
||||
RD::DataFormat format = p_render_buffers->get_base_data_format();
|
||||
uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
uint32_t layers = 1; // we only need one layer, in multiview we're processing one layer at a time.
|
||||
|
||||
p_render_buffers->create_texture(SNAME("FSR"), SNAME("upscale_texture"), format, usage_bits, RD::TEXTURE_SAMPLES_1, target_size, layers);
|
||||
}
|
||||
|
||||
RID upscale_texture = p_render_buffers->get_texture(SNAME("FSR"), SNAME("upscale_texture"));
|
||||
|
||||
FSRUpscalePushConstant push_constant;
|
||||
memset(&push_constant, 0, sizeof(FSRUpscalePushConstant));
|
||||
|
||||
int dispatch_x = (target_size.x + 15) / 16;
|
||||
int dispatch_y = (target_size.y + 15) / 16;
|
||||
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
|
||||
|
||||
push_constant.resolution_width = internal_size.width;
|
||||
push_constant.resolution_height = internal_size.height;
|
||||
push_constant.upscaled_width = target_size.width;
|
||||
push_constant.upscaled_height = target_size.height;
|
||||
push_constant.sharpness = fsr_upscale_sharpness;
|
||||
|
||||
RID shader = fsr_shader.version_get_shader(shader_version, 0);
|
||||
ERR_FAIL_COND(shader.is_null());
|
||||
|
||||
RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
|
||||
|
||||
//FSR Easc
|
||||
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, p_source_rd_texture });
|
||||
RD::Uniform u_upscale_texture(RD::UNIFORM_TYPE_IMAGE, 0, { upscale_texture });
|
||||
|
||||
push_constant.pass = FSR_UPSCALE_PASS_EASU;
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_upscale_texture), 1);
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1);
|
||||
RD::get_singleton()->compute_list_add_barrier(compute_list);
|
||||
|
||||
//FSR Rcas
|
||||
RD::Uniform u_upscale_texture_with_sampler(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, { default_sampler, upscale_texture });
|
||||
RD::Uniform u_destination_texture(RD::UNIFORM_TYPE_IMAGE, 0, { p_destination_texture });
|
||||
|
||||
push_constant.pass = FSR_UPSCALE_PASS_RCAS;
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_upscale_texture_with_sampler), 0);
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_destination_texture), 1);
|
||||
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(FSRUpscalePushConstant));
|
||||
|
||||
RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1);
|
||||
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
72
engine/servers/rendering/renderer_rd/effects/fsr.h
Normal file
72
engine/servers/rendering/renderer_rd/effects/fsr.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FSR_RD_H
|
||||
#define FSR_RD_H
|
||||
|
||||
#include "../pipeline_cache_rd.h"
|
||||
#include "../storage_rd/render_scene_buffers_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class FSR {
|
||||
public:
|
||||
FSR();
|
||||
~FSR();
|
||||
|
||||
void fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_rd_texture, RID p_destination_texture);
|
||||
|
||||
private:
|
||||
enum FSRUpscalePass {
|
||||
FSR_UPSCALE_PASS_EASU = 0,
|
||||
FSR_UPSCALE_PASS_RCAS = 1
|
||||
};
|
||||
|
||||
struct FSRUpscalePushConstant {
|
||||
float resolution_width;
|
||||
float resolution_height;
|
||||
float upscaled_width;
|
||||
float upscaled_height;
|
||||
float sharpness;
|
||||
int pass;
|
||||
int _unused0, _unused1;
|
||||
};
|
||||
|
||||
FsrUpscaleShaderRD fsr_shader;
|
||||
RID shader_version;
|
||||
RID pipeline;
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
#endif // FSR_RD_H
|
||||
887
engine/servers/rendering/renderer_rd/effects/fsr2.cpp
Normal file
887
engine/servers/rendering/renderer_rd/effects/fsr2.cpp
Normal file
|
|
@ -0,0 +1,887 @@
|
|||
/**************************************************************************/
|
||||
/* 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 <wchar.h>
|
||||
#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 + floor(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;
|
||||
|
||||
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);
|
||||
|
||||
Vector<RD::Uniform> compute_uniforms;
|
||||
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;
|
||||
uint64_t default_subgroup_size = RD::get_singleton()->limit_get(RD::LIMIT_SUBGROUP_SIZE);
|
||||
capabilities.minimumSupportedShaderModel = FFX_SHADER_MODEL_5_1;
|
||||
capabilities.waveLaneCountMin = RD::get_singleton()->limit_get(RD::LIMIT_SUBGROUP_MIN_SIZE);
|
||||
capabilities.waveLaneCountMax = RD::get_singleton()->limit_get(RD::LIMIT_SUBGROUP_MAX_SIZE);
|
||||
capabilities.fp16Supported = RD::get_singleton()->has_feature(RD::Features::SUPPORTS_FSR_HALF_FLOAT);
|
||||
capabilities.raytracingSupported = false;
|
||||
|
||||
bool force_wave_64 = default_subgroup_size == 32 && capabilities.waveLaneCountMax == 64;
|
||||
bool use_lut = force_wave_64 || default_subgroup_size == 64;
|
||||
|
||||
String general_defines_base =
|
||||
"\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";
|
||||
|
||||
if (use_lut) {
|
||||
general_defines_base += "\n#define FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE 1\n";
|
||||
}
|
||||
|
||||
String general_defines = general_defines_base;
|
||||
if (capabilities.fp16Supported) {
|
||||
general_defines += "\n#define FFX_HALF 1\n";
|
||||
}
|
||||
|
||||
Vector<String> modes;
|
||||
modes.push_back("");
|
||||
|
||||
// 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, general_defines);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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, general_defines);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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, general_defines);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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;
|
||||
accumulate_modes.push_back("\n");
|
||||
accumulate_modes.push_back("\n#define FFX_FSR2_OPTION_APPLY_SHARPENING 1\n");
|
||||
|
||||
String general_defines_accumulate;
|
||||
if (RD::get_singleton()->get_device_vendor_name() == "NVIDIA") {
|
||||
// Workaround: Disable FP16 path for the accumulate pass on NVIDIA due to reduced occupancy and high VRAM throughput.
|
||||
general_defines_accumulate = general_defines_base;
|
||||
} else {
|
||||
general_defines_accumulate = general_defines;
|
||||
}
|
||||
|
||||
Pass &pass = device.passes[FFX_FSR2_PASS_ACCUMULATE];
|
||||
pass.shader = &shaders.accumulate;
|
||||
pass.shader->initialize(accumulate_modes, general_defines_accumulate);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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.
|
||||
Pass &sharpen_pass = device.passes[FFX_FSR2_PASS_ACCUMULATE_SHARPEN];
|
||||
sharpen_pass = pass;
|
||||
sharpen_pass.shader_variant = 1;
|
||||
}
|
||||
|
||||
{
|
||||
Pass &pass = device.passes[FFX_FSR2_PASS_RCAS];
|
||||
pass.shader = &shaders.rcas;
|
||||
pass.shader->initialize(modes, general_defines_base);
|
||||
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, general_defines_base);
|
||||
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, general_defines);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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, general_defines);
|
||||
pass.shader_version = pass.shader->version_create();
|
||||
|
||||
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++) {
|
||||
if (device.passes[i].pipeline.pipeline_rid.is_valid()) {
|
||||
RD::get_singleton()->free(device.passes[i].pipeline.pipeline_rid);
|
||||
}
|
||||
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);
|
||||
}
|
||||
199
engine/servers/rendering/renderer_rd/effects/fsr2.h
Normal file
199
engine/servers/rendering/renderer_rd/effects/fsr2.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FSR2_RD_H
|
||||
#define FSR2_RD_H
|
||||
|
||||
#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
|
||||
|
||||
#endif // FSR2_RD_H
|
||||
254
engine/servers/rendering/renderer_rd/effects/luminance.cpp
Normal file
254
engine/servers/rendering/renderer_rd/effects/luminance.cpp
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD);
|
||||
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]);
|
||||
}
|
||||
120
engine/servers/rendering/renderer_rd/effects/luminance.h
Normal file
120
engine/servers/rendering/renderer_rd/effects/luminance.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LUMINANCE_RD_H
|
||||
#define LUMINANCE_RD_H
|
||||
|
||||
#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"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // LUMINANCE_RD_H
|
||||
130
engine/servers/rendering/renderer_rd/effects/resolve.cpp
Normal file
130
engine/servers/rendering/renderer_rd/effects/resolve.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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;
|
||||
|
||||
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();
|
||||
}
|
||||
74
engine/servers/rendering/renderer_rd/effects/resolve.h
Normal file
74
engine/servers/rendering/renderer_rd/effects/resolve.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RESOLVE_RD_H
|
||||
#define RESOLVE_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/resolve.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // RESOLVE_RD_H
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ROUGHNESS_LIMITER_RD_H
|
||||
#define ROUGHNESS_LIMITER_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/roughness_limiter.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // ROUGHNESS_LIMITER_RD_H
|
||||
124
engine/servers/rendering/renderer_rd/effects/sort_effects.cpp
Normal file
124
engine/servers/rendering/renderer_rd/effects/sort_effects.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
#include "servers/rendering/renderer_rd/uniform_set_cache_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();
|
||||
}
|
||||
71
engine/servers/rendering/renderer_rd/effects/sort_effects.h
Normal file
71
engine/servers/rendering/renderer_rd/effects/sort_effects.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SORT_EFFECTS_RD_H
|
||||
#define SORT_EFFECTS_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/sort.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.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
|
||||
|
||||
#endif // SORT_EFFECTS_RD_H
|
||||
1688
engine/servers/rendering/renderer_rd/effects/ss_effects.cpp
Normal file
1688
engine/servers/rendering/renderer_rd/effects/ss_effects.cpp
Normal file
File diff suppressed because it is too large
Load diff
533
engine/servers/rendering/renderer_rd/effects/ss_effects.h
Normal file
533
engine/servers/rendering/renderer_rd/effects/ss_effects.h
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SS_EFFECTS_RD_H
|
||||
#define SS_EFFECTS_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssao.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssao_importance_map.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssao_interleave.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssil.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssil_blur.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssil_importance_map.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/ssil_interleave.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
#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
|
||||
|
||||
#endif // SS_EFFECTS_RD_H
|
||||
124
engine/servers/rendering/renderer_rd/effects/taa.cpp
Normal file
124
engine/servers/rendering/renderer_rd/effects/taa.cpp
Normal 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 = 0.025f;
|
||||
push_constant.disocclusion_scale = 10.0f;
|
||||
|
||||
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
|
||||
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline);
|
||||
|
||||
RD::Uniform u_frame_source(RD::UNIFORM_TYPE_IMAGE, 0, { p_frame });
|
||||
RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, { default_sampler, p_depth });
|
||||
RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 2, { p_velocity });
|
||||
RD::Uniform u_prev_velocity(RD::UNIFORM_TYPE_IMAGE, 3, { p_prev_velocity });
|
||||
RD::Uniform u_history(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 4, { default_sampler, p_history });
|
||||
RD::Uniform u_frame_dest(RD::UNIFORM_TYPE_IMAGE, 5, { p_temp });
|
||||
|
||||
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_frame_source, u_depth, u_velocity, u_prev_velocity, u_history, u_frame_dest), 0);
|
||||
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(TAAResolvePushConstant));
|
||||
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_resolution.width, p_resolution.height, 1);
|
||||
RD::get_singleton()->compute_list_end();
|
||||
}
|
||||
|
||||
void TAA::process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far) {
|
||||
CopyEffects *copy_effects = CopyEffects::get_singleton();
|
||||
|
||||
uint32_t view_count = p_render_buffers->get_view_count();
|
||||
Size2i internal_size = p_render_buffers->get_internal_size();
|
||||
Size2i target_size = p_render_buffers->get_target_size();
|
||||
|
||||
bool just_allocated = false;
|
||||
if (!p_render_buffers->has_texture(SNAME("taa"), SNAME("history"))) {
|
||||
uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
|
||||
|
||||
p_render_buffers->create_texture(SNAME("taa"), SNAME("history"), p_format, usage_bits);
|
||||
p_render_buffers->create_texture(SNAME("taa"), SNAME("temp"), p_format, usage_bits);
|
||||
|
||||
p_render_buffers->create_texture(SNAME("taa"), SNAME("prev_velocity"), RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits);
|
||||
|
||||
just_allocated = true;
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_begin_label("TAA");
|
||||
|
||||
for (uint32_t v = 0; v < view_count; v++) {
|
||||
// Get our (cached) slices
|
||||
RID internal_texture = p_render_buffers->get_internal_texture(v);
|
||||
RID velocity_buffer = p_render_buffers->get_velocity_buffer(false, v);
|
||||
RID taa_history = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("history"), v, 0);
|
||||
RID taa_prev_velocity = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("prev_velocity"), v, 0);
|
||||
|
||||
if (!just_allocated) {
|
||||
RID depth_texture = p_render_buffers->get_depth_texture(v);
|
||||
RID taa_temp = p_render_buffers->get_texture_slice(SNAME("taa"), SNAME("temp"), v, 0);
|
||||
resolve(internal_texture, taa_temp, depth_texture, velocity_buffer, taa_prev_velocity, taa_history, Size2(internal_size.x, internal_size.y), p_z_near, p_z_far);
|
||||
copy_effects->copy_to_rect(taa_temp, internal_texture, Rect2(0, 0, internal_size.x, internal_size.y));
|
||||
}
|
||||
|
||||
copy_effects->copy_to_rect(internal_texture, taa_history, Rect2(0, 0, internal_size.x, internal_size.y));
|
||||
copy_effects->copy_to_rect(velocity_buffer, taa_prev_velocity, Rect2(0, 0, target_size.x, target_size.y));
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
67
engine/servers/rendering/renderer_rd/effects/taa.h
Normal file
67
engine/servers/rendering/renderer_rd/effects/taa.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TAA_RD_H
|
||||
#define TAA_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class TAA {
|
||||
public:
|
||||
TAA();
|
||||
~TAA();
|
||||
|
||||
void process(Ref<RenderSceneBuffersRD> p_render_buffers, RD::DataFormat p_format, float p_z_near, float p_z_far);
|
||||
|
||||
private:
|
||||
struct TAAResolvePushConstant {
|
||||
float resolution_width;
|
||||
float resolution_height;
|
||||
float disocclusion_threshold;
|
||||
float disocclusion_scale;
|
||||
};
|
||||
|
||||
TaaResolveShaderRD taa_shader;
|
||||
RID shader_version;
|
||||
RID pipeline;
|
||||
|
||||
void resolve(RID p_frame, RID p_temp, RID p_depth, RID p_velocity, RID p_prev_velocity, RID p_history, Size2 p_resolution, float p_z_near, float p_z_far);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
#endif // TAA_RD_H
|
||||
259
engine/servers/rendering/renderer_rd/effects/tone_mapper.cpp
Normal file
259
engine/servers/rendering/renderer_rd/effects/tone_mapper.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
|
||||
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::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD);
|
||||
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;
|
||||
|
||||
tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0;
|
||||
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);
|
||||
}
|
||||
159
engine/servers/rendering/renderer_rd/effects/tone_mapper.h
Normal file
159
engine/servers/rendering/renderer_rd/effects/tone_mapper.h
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TONE_MAPPER_RD_H
|
||||
#define TONE_MAPPER_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/tonemap.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.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_DEBANDING = (1 << 5),
|
||||
TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 6),
|
||||
};
|
||||
|
||||
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;
|
||||
bool use_debanding = false;
|
||||
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
|
||||
|
||||
#endif // TONE_MAPPER_RD_H
|
||||
162
engine/servers/rendering/renderer_rd/effects/vrs.cpp
Normal file
162
engine/servers/rendering/renderer_rd/effects/vrs.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/**************************************************************************/
|
||||
/* 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 _3D_DISABLED
|
||||
#include "servers/xr_server.h"
|
||||
#endif // _3D_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_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 }));
|
||||
|
||||
VRSPushConstant push_constant = {};
|
||||
|
||||
int mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT;
|
||||
|
||||
// Set maximum texel factor based on maximum fragment size, some GPUs do not support 8x8 (fragment shading rate approach).
|
||||
if (MIN(RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_WIDTH), RD::get_singleton()->limit_get(RD::LIMIT_VRS_MAX_FRAGMENT_HEIGHT)) > 4) {
|
||||
push_constant.max_texel_factor = 3.0;
|
||||
} else {
|
||||
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::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>());
|
||||
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 {
|
||||
int32_t texel_width = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_WIDTH);
|
||||
int32_t texel_height = RD::get_singleton()->limit_get(RD::LIMIT_VRS_TEXEL_HEIGHT);
|
||||
|
||||
int width = p_base_size.x / texel_width;
|
||||
if (p_base_size.x % texel_width != 0) {
|
||||
width++;
|
||||
}
|
||||
int height = p_base_size.y / texel_height;
|
||||
if (p_base_size.y % texel_height != 0) {
|
||||
height++;
|
||||
}
|
||||
return Size2i(width, height);
|
||||
}
|
||||
|
||||
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 _3D_DISABLED
|
||||
} else if (vrs_mode == RS::VIEWPORT_VRS_XR) {
|
||||
Ref<XRInterface> interface = XRServer::get_singleton()->get_primary_interface();
|
||||
if (interface.is_valid()) {
|
||||
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 // _3D_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();
|
||||
}
|
||||
}
|
||||
76
engine/servers/rendering/renderer_rd/effects/vrs.h
Normal file
76
engine/servers/rendering/renderer_rd/effects/vrs.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef VRS_RD_H
|
||||
#define VRS_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/effects/vrs.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class VRS {
|
||||
private:
|
||||
enum VRSMode {
|
||||
VRS_DEFAULT,
|
||||
VRS_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
|
||||
|
||||
#endif // VRS_RD_H
|
||||
5
engine/servers/rendering/renderer_rd/environment/SCsub
Normal file
5
engine/servers/rendering/renderer_rd/environment/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
1147
engine/servers/rendering/renderer_rd/environment/fog.cpp
Normal file
1147
engine/servers/rendering/renderer_rd/environment/fog.cpp
Normal file
File diff suppressed because it is too large
Load diff
359
engine/servers/rendering/renderer_rd/environment/fog.h
Normal file
359
engine/servers/rendering/renderer_rd/environment/fog.h
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
/**************************************************************************/
|
||||
/* fog.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FOG_RD_H
|
||||
#define FOG_RD_H
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/environment/renderer_fog.h"
|
||||
#include "servers/rendering/renderer_rd/cluster_builder_rd.h"
|
||||
#include "servers/rendering/renderer_rd/environment/gi.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h"
|
||||
#include "servers/rendering/storage/utilities.h"
|
||||
|
||||
#define RB_SCOPE_FOG SNAME("Fog")
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class Fog : public RendererFog {
|
||||
private:
|
||||
static Fog *singleton;
|
||||
|
||||
/* FOG VOLUMES */
|
||||
|
||||
struct FogVolume {
|
||||
RID material;
|
||||
Vector3 size = Vector3(2, 2, 2);
|
||||
|
||||
RS::FogVolumeShape shape = RS::FOG_VOLUME_SHAPE_BOX;
|
||||
|
||||
Dependency dependency;
|
||||
};
|
||||
|
||||
mutable RID_Owner<FogVolume, true> fog_volume_owner;
|
||||
|
||||
struct FogVolumeInstance {
|
||||
RID volume;
|
||||
Transform3D transform;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
mutable RID_Owner<FogVolumeInstance> fog_volume_instance_owner;
|
||||
|
||||
const int SAMPLERS_BINDING_FIRST_INDEX = 3;
|
||||
|
||||
/* Volumetric Fog */
|
||||
struct VolumetricFogShader {
|
||||
enum FogSet {
|
||||
FOG_SET_BASE,
|
||||
FOG_SET_UNIFORMS,
|
||||
FOG_SET_MATERIAL,
|
||||
FOG_SET_MAX,
|
||||
};
|
||||
|
||||
struct FogPushConstant {
|
||||
float position[3];
|
||||
float pad;
|
||||
|
||||
float size[3];
|
||||
float pad2;
|
||||
|
||||
int32_t corner[3];
|
||||
uint32_t shape;
|
||||
|
||||
float transform[16];
|
||||
};
|
||||
|
||||
struct VolumeUBO {
|
||||
float fog_frustum_size_begin[2];
|
||||
float fog_frustum_size_end[2];
|
||||
|
||||
float fog_frustum_end;
|
||||
float z_near;
|
||||
float z_far;
|
||||
float time;
|
||||
|
||||
int32_t fog_volume_size[3];
|
||||
uint32_t directional_light_count;
|
||||
|
||||
uint32_t use_temporal_reprojection;
|
||||
uint32_t temporal_frame;
|
||||
float detail_spread;
|
||||
float temporal_blend;
|
||||
|
||||
float to_prev_view[16];
|
||||
float transform[16];
|
||||
};
|
||||
|
||||
ShaderCompiler compiler;
|
||||
VolumetricFogShaderRD shader;
|
||||
RID volume_ubo;
|
||||
|
||||
RID default_shader;
|
||||
RID default_material;
|
||||
RID default_shader_rd;
|
||||
|
||||
RID base_uniform_set;
|
||||
|
||||
RID params_ubo;
|
||||
|
||||
enum {
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY,
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_DENSITY_WITH_SDFGI,
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_FILTER,
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_FOG,
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_COPY,
|
||||
VOLUMETRIC_FOG_PROCESS_SHADER_MAX,
|
||||
};
|
||||
|
||||
struct ParamsUBO {
|
||||
float fog_frustum_size_begin[2];
|
||||
float fog_frustum_size_end[2];
|
||||
|
||||
float fog_frustum_end;
|
||||
float ambient_inject;
|
||||
float z_far;
|
||||
uint32_t filter_axis;
|
||||
|
||||
float ambient_color[3];
|
||||
float sky_contribution;
|
||||
|
||||
int32_t fog_volume_size[3];
|
||||
uint32_t directional_light_count;
|
||||
|
||||
float base_emission[3];
|
||||
float base_density;
|
||||
|
||||
float base_scattering[3];
|
||||
float phase_g;
|
||||
|
||||
float detail_spread;
|
||||
float gi_inject;
|
||||
uint32_t max_voxel_gi_instances;
|
||||
uint32_t cluster_type_size;
|
||||
|
||||
float screen_size[2];
|
||||
uint32_t cluster_shift;
|
||||
uint32_t cluster_width;
|
||||
|
||||
uint32_t max_cluster_element_count_div_32;
|
||||
uint32_t use_temporal_reprojection;
|
||||
uint32_t temporal_frame;
|
||||
float temporal_blend;
|
||||
|
||||
float cam_rotation[12];
|
||||
float to_prev_view[16];
|
||||
float radiance_inverse_xform[12];
|
||||
};
|
||||
|
||||
VolumetricFogProcessShaderRD process_shader;
|
||||
|
||||
RID process_shader_version;
|
||||
RID process_pipelines[VOLUMETRIC_FOG_PROCESS_SHADER_MAX];
|
||||
|
||||
} volumetric_fog;
|
||||
|
||||
Vector3i _point_get_position_in_froxel_volume(const Vector3 &p_point, float fog_end, const Vector2 &fog_near_size, const Vector2 &fog_far_size, float volumetric_fog_detail_spread, const Vector3 &fog_size, const Transform3D &p_cam_transform);
|
||||
|
||||
struct FogShaderData : public RendererRD::MaterialStorage::ShaderData {
|
||||
bool valid = false;
|
||||
RID version;
|
||||
|
||||
RID pipeline;
|
||||
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size = 0;
|
||||
|
||||
String code;
|
||||
|
||||
bool uses_time = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
|
||||
|
||||
FogShaderData() {}
|
||||
virtual ~FogShaderData();
|
||||
};
|
||||
|
||||
struct FogMaterialData : public RendererRD::MaterialStorage::MaterialData {
|
||||
FogShaderData *shader_data = nullptr;
|
||||
RID uniform_set;
|
||||
bool uniform_set_updated;
|
||||
|
||||
virtual void set_render_priority(int p_priority) {}
|
||||
virtual void set_next_pass(RID p_pass) {}
|
||||
virtual bool update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~FogMaterialData();
|
||||
};
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *_create_fog_shader_func();
|
||||
static RendererRD::MaterialStorage::ShaderData *_create_fog_shader_funcs();
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *_create_fog_material_func(FogShaderData *p_shader);
|
||||
static RendererRD::MaterialStorage::MaterialData *_create_fog_material_funcs(RendererRD::MaterialStorage::ShaderData *p_shader);
|
||||
|
||||
public:
|
||||
static Fog *get_singleton() { return singleton; }
|
||||
|
||||
Fog();
|
||||
~Fog();
|
||||
|
||||
/* FOG VOLUMES */
|
||||
|
||||
bool owns_fog_volume(RID p_rid) { return fog_volume_owner.owns(p_rid); };
|
||||
|
||||
virtual RID fog_volume_allocate() override;
|
||||
virtual void fog_volume_initialize(RID p_rid) override;
|
||||
virtual void fog_volume_free(RID p_rid) override;
|
||||
Dependency *fog_volume_get_dependency(RID p_fog_volume) const;
|
||||
|
||||
virtual void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override;
|
||||
virtual void fog_volume_set_size(RID p_fog_volume, const Vector3 &p_size) override;
|
||||
virtual void fog_volume_set_material(RID p_fog_volume, RID p_material) override;
|
||||
virtual RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override;
|
||||
RID fog_volume_get_material(RID p_fog_volume) const;
|
||||
virtual AABB fog_volume_get_aabb(RID p_fog_volume) const override;
|
||||
Vector3 fog_volume_get_size(RID p_fog_volume) const;
|
||||
|
||||
/* FOG VOLUMES INSTANCE */
|
||||
|
||||
bool owns_fog_volume_instance(RID p_rid) { return fog_volume_instance_owner.owns(p_rid); };
|
||||
|
||||
RID fog_volume_instance_create(RID p_fog_volume);
|
||||
void fog_instance_free(RID p_rid);
|
||||
|
||||
void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) {
|
||||
Fog::FogVolumeInstance *fvi = fog_volume_instance_owner.get_or_null(p_fog_volume_instance);
|
||||
ERR_FAIL_NULL(fvi);
|
||||
fvi->transform = p_transform;
|
||||
}
|
||||
|
||||
void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) {
|
||||
Fog::FogVolumeInstance *fvi = fog_volume_instance_owner.get_or_null(p_fog_volume_instance);
|
||||
ERR_FAIL_NULL(fvi);
|
||||
fvi->active = p_active;
|
||||
}
|
||||
|
||||
RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const {
|
||||
Fog::FogVolumeInstance *fvi = fog_volume_instance_owner.get_or_null(p_fog_volume_instance);
|
||||
ERR_FAIL_NULL_V(fvi, RID());
|
||||
return fvi->volume;
|
||||
}
|
||||
|
||||
Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const {
|
||||
Fog::FogVolumeInstance *fvi = fog_volume_instance_owner.get_or_null(p_fog_volume_instance);
|
||||
ERR_FAIL_NULL_V(fvi, Vector3());
|
||||
return fvi->transform.get_origin();
|
||||
}
|
||||
|
||||
/* Volumetric FOG */
|
||||
class VolumetricFog : public RenderBufferCustomDataRD {
|
||||
GDCLASS(VolumetricFog, RenderBufferCustomDataRD)
|
||||
|
||||
public:
|
||||
enum {
|
||||
MAX_TEMPORAL_FRAMES = 16
|
||||
};
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 0;
|
||||
|
||||
float length;
|
||||
float spread;
|
||||
|
||||
RID light_density_map;
|
||||
RID prev_light_density_map;
|
||||
RID fog_map;
|
||||
RID density_map;
|
||||
RID light_map;
|
||||
RID emissive_map;
|
||||
|
||||
RID fog_uniform_set;
|
||||
RID copy_uniform_set;
|
||||
|
||||
struct {
|
||||
RID process_uniform_set_density;
|
||||
RID process_uniform_set;
|
||||
RID process_uniform_set2;
|
||||
} gi_dependent_sets;
|
||||
|
||||
RID sdfgi_uniform_set;
|
||||
RID sky_uniform_set;
|
||||
|
||||
int last_shadow_filter = -1;
|
||||
|
||||
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{};
|
||||
virtual void free_data() override{};
|
||||
|
||||
bool sync_gi_dependent_sets_validity(bool p_ensure_freed = false);
|
||||
|
||||
void init(const Vector3i &fog_size, RID p_sky_shader);
|
||||
~VolumetricFog();
|
||||
};
|
||||
|
||||
void init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_layers, bool p_is_using_radiance_cubemap_array);
|
||||
void free_fog_shader();
|
||||
|
||||
struct VolumetricFogSettings {
|
||||
Vector2i rb_size;
|
||||
double time;
|
||||
bool is_using_radiance_cubemap_array;
|
||||
uint32_t max_cluster_elements;
|
||||
bool volumetric_fog_filter_active;
|
||||
RID shadow_sampler;
|
||||
RID voxel_gi_buffer;
|
||||
RID shadow_atlas_depth;
|
||||
RID omni_light_buffer;
|
||||
RID spot_light_buffer;
|
||||
RID directional_shadow_depth;
|
||||
RID directional_light_buffer;
|
||||
|
||||
// Objects related to our render buffer
|
||||
Ref<VolumetricFog> vfog;
|
||||
ClusterBuilderRD *cluster_builder;
|
||||
GI *gi;
|
||||
Ref<GI::SDFGI> sdfgi;
|
||||
Ref<GI::RenderBuffersGI> rbgi;
|
||||
RID env;
|
||||
SkyRD *sky;
|
||||
};
|
||||
void volumetric_fog_update(const VolumetricFogSettings &p_settings, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
#endif // FOG_RD_H
|
||||
4114
engine/servers/rendering/renderer_rd/environment/gi.cpp
Normal file
4114
engine/servers/rendering/renderer_rd/environment/gi.cpp
Normal file
File diff suppressed because it is too large
Load diff
833
engine/servers/rendering/renderer_rd/environment/gi.h
Normal file
833
engine/servers/rendering/renderer_rd/environment/gi.h
Normal file
|
|
@ -0,0 +1,833 @@
|
|||
/**************************************************************************/
|
||||
/* gi.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GI_RD_H
|
||||
#define GI_RD_H
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/environment/renderer_gi.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/renderer_rd/environment/sky.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/gi.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_buffer_custom_data_rd.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/storage/utilities.h"
|
||||
|
||||
#define RB_SCOPE_GI SNAME("rbgi")
|
||||
#define RB_SCOPE_SDFGI SNAME("sdfgi")
|
||||
|
||||
#define RB_TEX_AMBIENT SNAME("ambient")
|
||||
#define RB_TEX_REFLECTION SNAME("reflection")
|
||||
|
||||
// Forward declare RenderDataRD and RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound
|
||||
class RenderDataRD;
|
||||
class RendererSceneRenderRD;
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class GI : public RendererGI {
|
||||
public:
|
||||
/* VOXEL GI STORAGE */
|
||||
|
||||
struct VoxelGI {
|
||||
RID octree_buffer;
|
||||
RID data_buffer;
|
||||
RID sdf_texture;
|
||||
|
||||
uint32_t octree_buffer_size = 0;
|
||||
uint32_t data_buffer_size = 0;
|
||||
|
||||
Vector<int> level_counts;
|
||||
|
||||
int cell_count = 0;
|
||||
|
||||
Transform3D to_cell_xform;
|
||||
AABB bounds;
|
||||
Vector3i octree_size;
|
||||
|
||||
float dynamic_range = 2.0;
|
||||
float energy = 1.0;
|
||||
float baked_exposure = 1.0;
|
||||
float bias = 1.4;
|
||||
float normal_bias = 0.0;
|
||||
float propagation = 0.5;
|
||||
bool interior = false;
|
||||
bool use_two_bounces = true;
|
||||
|
||||
uint32_t version = 1;
|
||||
uint32_t data_version = 1;
|
||||
|
||||
Dependency dependency;
|
||||
};
|
||||
|
||||
/* VOXEL_GI INSTANCE */
|
||||
|
||||
//@TODO VoxelGIInstance is still directly used in the render code, we'll address this when we refactor the render code itself.
|
||||
|
||||
struct VoxelGIInstance {
|
||||
// access to our containers
|
||||
GI *gi = nullptr;
|
||||
|
||||
RID probe;
|
||||
RID texture;
|
||||
RID write_buffer;
|
||||
|
||||
struct Mipmap {
|
||||
RID texture;
|
||||
RID uniform_set;
|
||||
RID second_bounce_uniform_set;
|
||||
RID write_uniform_set;
|
||||
uint32_t level;
|
||||
uint32_t cell_offset;
|
||||
uint32_t cell_count;
|
||||
};
|
||||
Vector<Mipmap> mipmaps;
|
||||
|
||||
struct DynamicMap {
|
||||
RID texture; //color normally, or emission on first pass
|
||||
RID fb_depth; //actual depth buffer for the first pass, float depth for later passes
|
||||
RID depth; //actual depth buffer for the first pass, float depth for later passes
|
||||
RID normal; //normal buffer for the first pass
|
||||
RID albedo; //emission buffer for the first pass
|
||||
RID orm; //orm buffer for the first pass
|
||||
RID fb; //used for rendering, only valid on first map
|
||||
RID uniform_set;
|
||||
uint32_t size;
|
||||
int mipmap; // mipmap to write to, -1 if no mipmap assigned
|
||||
};
|
||||
|
||||
Vector<DynamicMap> dynamic_maps;
|
||||
|
||||
int slot = -1;
|
||||
uint32_t last_probe_version = 0;
|
||||
uint32_t last_probe_data_version = 0;
|
||||
|
||||
//uint64_t last_pass = 0;
|
||||
uint32_t render_index = 0;
|
||||
|
||||
bool has_dynamic_object_data = false;
|
||||
|
||||
Transform3D transform;
|
||||
|
||||
void update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects);
|
||||
void debug(RD::DrawListID p_draw_list, RID p_framebuffer, const Projection &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
|
||||
void free_resources();
|
||||
};
|
||||
|
||||
private:
|
||||
static GI *singleton;
|
||||
|
||||
/* VOXEL GI STORAGE */
|
||||
|
||||
mutable RID_Owner<VoxelGI, true> voxel_gi_owner;
|
||||
|
||||
/* VOXEL_GI INSTANCE */
|
||||
|
||||
mutable RID_Owner<VoxelGIInstance> voxel_gi_instance_owner;
|
||||
|
||||
struct VoxelGILight {
|
||||
uint32_t type;
|
||||
float energy;
|
||||
float radius;
|
||||
float attenuation;
|
||||
|
||||
float color[3];
|
||||
float cos_spot_angle;
|
||||
|
||||
float position[3];
|
||||
float inv_spot_attenuation;
|
||||
|
||||
float direction[3];
|
||||
uint32_t has_shadow;
|
||||
};
|
||||
|
||||
struct VoxelGIPushConstant {
|
||||
int32_t limits[3];
|
||||
uint32_t stack_size;
|
||||
|
||||
float emission_scale;
|
||||
float propagation;
|
||||
float dynamic_range;
|
||||
uint32_t light_count;
|
||||
|
||||
uint32_t cell_offset;
|
||||
uint32_t cell_count;
|
||||
float aniso_strength;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
struct VoxelGIDynamicPushConstant {
|
||||
int32_t limits[3];
|
||||
uint32_t light_count;
|
||||
int32_t x_dir[3];
|
||||
float z_base;
|
||||
int32_t y_dir[3];
|
||||
float z_sign;
|
||||
int32_t z_dir[3];
|
||||
float pos_multiplier;
|
||||
uint32_t rect_pos[2];
|
||||
uint32_t rect_size[2];
|
||||
uint32_t prev_rect_ofs[2];
|
||||
uint32_t prev_rect_size[2];
|
||||
uint32_t flip_x;
|
||||
uint32_t flip_y;
|
||||
float dynamic_range;
|
||||
uint32_t on_mipmap;
|
||||
float propagation;
|
||||
float pad[3];
|
||||
};
|
||||
|
||||
VoxelGILight *voxel_gi_lights = nullptr;
|
||||
uint32_t voxel_gi_max_lights = 32;
|
||||
RID voxel_gi_lights_uniform;
|
||||
|
||||
enum {
|
||||
VOXEL_GI_SHADER_VERSION_COMPUTE_LIGHT,
|
||||
VOXEL_GI_SHADER_VERSION_COMPUTE_SECOND_BOUNCE,
|
||||
VOXEL_GI_SHADER_VERSION_COMPUTE_MIPMAP,
|
||||
VOXEL_GI_SHADER_VERSION_WRITE_TEXTURE,
|
||||
VOXEL_GI_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING,
|
||||
VOXEL_GI_SHADER_VERSION_DYNAMIC_SHRINK_WRITE,
|
||||
VOXEL_GI_SHADER_VERSION_DYNAMIC_SHRINK_PLOT,
|
||||
VOXEL_GI_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT,
|
||||
VOXEL_GI_SHADER_VERSION_MAX
|
||||
};
|
||||
|
||||
VoxelGiShaderRD voxel_gi_shader;
|
||||
RID voxel_gi_lighting_shader_version;
|
||||
RID voxel_gi_lighting_shader_version_shaders[VOXEL_GI_SHADER_VERSION_MAX];
|
||||
RID voxel_gi_lighting_shader_version_pipelines[VOXEL_GI_SHADER_VERSION_MAX];
|
||||
|
||||
enum {
|
||||
VOXEL_GI_DEBUG_COLOR,
|
||||
VOXEL_GI_DEBUG_LIGHT,
|
||||
VOXEL_GI_DEBUG_EMISSION,
|
||||
VOXEL_GI_DEBUG_LIGHT_FULL,
|
||||
VOXEL_GI_DEBUG_MAX
|
||||
};
|
||||
|
||||
struct VoxelGIDebugPushConstant {
|
||||
float projection[16];
|
||||
uint32_t cell_offset;
|
||||
float dynamic_range;
|
||||
float alpha;
|
||||
uint32_t level;
|
||||
int32_t bounds[3];
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
VoxelGiDebugShaderRD voxel_gi_debug_shader;
|
||||
RID voxel_gi_debug_shader_version;
|
||||
RID voxel_gi_debug_shader_version_shaders[VOXEL_GI_DEBUG_MAX];
|
||||
PipelineCacheRD voxel_gi_debug_shader_version_pipelines[VOXEL_GI_DEBUG_MAX];
|
||||
RID voxel_gi_debug_uniform_set;
|
||||
|
||||
/* SDFGI */
|
||||
|
||||
struct SDFGIShader {
|
||||
enum SDFGIPreprocessShaderVersion {
|
||||
PRE_PROCESS_SCROLL,
|
||||
PRE_PROCESS_SCROLL_OCCLUSION,
|
||||
PRE_PROCESS_JUMP_FLOOD_INITIALIZE,
|
||||
PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF,
|
||||
PRE_PROCESS_JUMP_FLOOD,
|
||||
PRE_PROCESS_JUMP_FLOOD_OPTIMIZED,
|
||||
PRE_PROCESS_JUMP_FLOOD_UPSCALE,
|
||||
PRE_PROCESS_OCCLUSION,
|
||||
PRE_PROCESS_STORE,
|
||||
PRE_PROCESS_MAX
|
||||
};
|
||||
|
||||
struct PreprocessPushConstant {
|
||||
int32_t scroll[3];
|
||||
int32_t grid_size;
|
||||
|
||||
int32_t probe_offset[3];
|
||||
int32_t step_size;
|
||||
|
||||
int32_t half_size;
|
||||
uint32_t occlusion_index;
|
||||
int32_t cascade;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
SdfgiPreprocessShaderRD preprocess;
|
||||
RID preprocess_shader;
|
||||
RID preprocess_pipeline[PRE_PROCESS_MAX];
|
||||
|
||||
struct DebugPushConstant {
|
||||
float grid_size[3];
|
||||
uint32_t max_cascades;
|
||||
|
||||
int32_t screen_size[2];
|
||||
float y_mult;
|
||||
|
||||
float z_near;
|
||||
|
||||
float inv_projection[3][4];
|
||||
float cam_basis[3][3];
|
||||
float cam_origin[3];
|
||||
};
|
||||
|
||||
SdfgiDebugShaderRD debug;
|
||||
RID debug_shader;
|
||||
RID debug_shader_version;
|
||||
RID debug_pipeline;
|
||||
|
||||
enum ProbeDebugMode {
|
||||
PROBE_DEBUG_PROBES,
|
||||
PROBE_DEBUG_PROBES_MULTIVIEW,
|
||||
PROBE_DEBUG_VISIBILITY,
|
||||
PROBE_DEBUG_VISIBILITY_MULTIVIEW,
|
||||
PROBE_DEBUG_MAX
|
||||
};
|
||||
|
||||
struct DebugProbesSceneData {
|
||||
float projection[2][16];
|
||||
};
|
||||
|
||||
struct DebugProbesPushConstant {
|
||||
uint32_t band_power;
|
||||
uint32_t sections_in_band;
|
||||
uint32_t band_mask;
|
||||
float section_arc;
|
||||
|
||||
float grid_size[3];
|
||||
uint32_t cascade;
|
||||
|
||||
uint32_t pad;
|
||||
float y_mult;
|
||||
int32_t probe_debug_index;
|
||||
int32_t probe_axis_size;
|
||||
};
|
||||
|
||||
SdfgiDebugProbesShaderRD debug_probes;
|
||||
RID debug_probes_shader;
|
||||
RID debug_probes_shader_version;
|
||||
|
||||
PipelineCacheRD debug_probes_pipeline[PROBE_DEBUG_MAX];
|
||||
|
||||
struct Light {
|
||||
float color[3];
|
||||
float energy;
|
||||
|
||||
float direction[3];
|
||||
uint32_t has_shadow;
|
||||
|
||||
float position[3];
|
||||
float attenuation;
|
||||
|
||||
uint32_t type;
|
||||
float cos_spot_angle;
|
||||
float inv_spot_attenuation;
|
||||
float radius;
|
||||
};
|
||||
|
||||
struct DirectLightPushConstant {
|
||||
float grid_size[3];
|
||||
uint32_t max_cascades;
|
||||
|
||||
uint32_t cascade;
|
||||
uint32_t light_count;
|
||||
uint32_t process_offset;
|
||||
uint32_t process_increment;
|
||||
|
||||
int32_t probe_axis_size;
|
||||
float bounce_feedback;
|
||||
float y_mult;
|
||||
uint32_t use_occlusion;
|
||||
};
|
||||
|
||||
enum {
|
||||
DIRECT_LIGHT_MODE_STATIC,
|
||||
DIRECT_LIGHT_MODE_DYNAMIC,
|
||||
DIRECT_LIGHT_MODE_MAX
|
||||
};
|
||||
SdfgiDirectLightShaderRD direct_light;
|
||||
RID direct_light_shader;
|
||||
RID direct_light_pipeline[DIRECT_LIGHT_MODE_MAX];
|
||||
|
||||
enum {
|
||||
INTEGRATE_MODE_PROCESS,
|
||||
INTEGRATE_MODE_STORE,
|
||||
INTEGRATE_MODE_SCROLL,
|
||||
INTEGRATE_MODE_SCROLL_STORE,
|
||||
INTEGRATE_MODE_MAX
|
||||
};
|
||||
struct IntegratePushConstant {
|
||||
enum {
|
||||
SKY_MODE_DISABLED,
|
||||
SKY_MODE_COLOR,
|
||||
SKY_MODE_SKY,
|
||||
};
|
||||
|
||||
float grid_size[3];
|
||||
uint32_t max_cascades;
|
||||
|
||||
uint32_t probe_axis_size;
|
||||
uint32_t cascade;
|
||||
uint32_t history_index;
|
||||
uint32_t history_size;
|
||||
|
||||
uint32_t ray_count;
|
||||
float ray_bias;
|
||||
int32_t image_size[2];
|
||||
|
||||
int32_t world_offset[3];
|
||||
uint32_t sky_mode;
|
||||
|
||||
int32_t scroll[3];
|
||||
float sky_energy;
|
||||
|
||||
float sky_color[3];
|
||||
float y_mult;
|
||||
|
||||
uint32_t store_ambient_texture;
|
||||
uint32_t pad[3];
|
||||
};
|
||||
|
||||
SdfgiIntegrateShaderRD integrate;
|
||||
RID integrate_shader;
|
||||
RID integrate_pipeline[INTEGRATE_MODE_MAX];
|
||||
|
||||
RID integrate_default_sky_uniform_set;
|
||||
|
||||
} sdfgi_shader;
|
||||
|
||||
public:
|
||||
static GI *get_singleton() { return singleton; }
|
||||
|
||||
/* GI */
|
||||
|
||||
enum {
|
||||
MAX_VOXEL_GI_INSTANCES = 8
|
||||
};
|
||||
|
||||
// Struct for use in render buffer
|
||||
class RenderBuffersGI : public RenderBufferCustomDataRD {
|
||||
GDCLASS(RenderBuffersGI, RenderBufferCustomDataRD)
|
||||
|
||||
private:
|
||||
RID voxel_gi_buffer;
|
||||
|
||||
public:
|
||||
RID voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];
|
||||
|
||||
RID full_buffer;
|
||||
RID full_dispatch;
|
||||
RID full_mask;
|
||||
|
||||
/* GI buffers */
|
||||
bool using_half_size_gi = false;
|
||||
|
||||
RID uniform_set[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
RID scene_data_ubo;
|
||||
|
||||
RID get_voxel_gi_buffer();
|
||||
|
||||
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{};
|
||||
virtual void free_data() override;
|
||||
};
|
||||
|
||||
/* VOXEL GI API */
|
||||
|
||||
bool owns_voxel_gi(RID p_rid) { return voxel_gi_owner.owns(p_rid); };
|
||||
|
||||
virtual RID voxel_gi_allocate() override;
|
||||
virtual void voxel_gi_free(RID p_voxel_gi) override;
|
||||
virtual void voxel_gi_initialize(RID p_voxel_gi) override;
|
||||
|
||||
virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
|
||||
|
||||
virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
|
||||
virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
|
||||
|
||||
virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
|
||||
virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) override;
|
||||
virtual float voxel_gi_get_energy(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override;
|
||||
virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) override;
|
||||
virtual float voxel_gi_get_bias(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
|
||||
virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
|
||||
virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
|
||||
|
||||
virtual uint32_t voxel_gi_get_version(RID p_probe) const override;
|
||||
uint32_t voxel_gi_get_data_version(RID p_probe);
|
||||
|
||||
RID voxel_gi_get_octree_buffer(RID p_voxel_gi) const;
|
||||
RID voxel_gi_get_data_buffer(RID p_voxel_gi) const;
|
||||
|
||||
RID voxel_gi_get_sdf_texture(RID p_voxel_gi);
|
||||
|
||||
Dependency *voxel_gi_get_dependency(RID p_voxel_gi) const;
|
||||
|
||||
/* VOXEL_GI INSTANCE */
|
||||
|
||||
_FORCE_INLINE_ RID voxel_gi_instance_get_texture(RID p_probe) {
|
||||
VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe);
|
||||
ERR_FAIL_NULL_V(voxel_gi, RID());
|
||||
return voxel_gi->texture;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ void voxel_gi_instance_set_render_index(RID p_probe, uint32_t p_index) {
|
||||
VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_probe);
|
||||
ERR_FAIL_NULL(voxel_gi);
|
||||
|
||||
voxel_gi->render_index = p_index;
|
||||
};
|
||||
|
||||
bool voxel_gi_instance_owns(RID p_rid) const {
|
||||
return voxel_gi_instance_owner.owns(p_rid);
|
||||
}
|
||||
|
||||
void voxel_gi_instance_free(RID p_rid);
|
||||
|
||||
RS::VoxelGIQuality voxel_gi_quality = RS::VOXEL_GI_QUALITY_LOW;
|
||||
|
||||
/* SDFGI */
|
||||
|
||||
class SDFGI : public RenderBufferCustomDataRD {
|
||||
GDCLASS(SDFGI, RenderBufferCustomDataRD)
|
||||
|
||||
public:
|
||||
enum {
|
||||
MAX_CASCADES = 8,
|
||||
CASCADE_SIZE = 128,
|
||||
PROBE_DIVISOR = 16,
|
||||
ANISOTROPY_SIZE = 6,
|
||||
MAX_DYNAMIC_LIGHTS = 128,
|
||||
MAX_STATIC_LIGHTS = 1024,
|
||||
LIGHTPROBE_OCT_SIZE = 6,
|
||||
SH_SIZE = 16
|
||||
};
|
||||
|
||||
struct Cascade {
|
||||
struct UBO {
|
||||
float offset[3];
|
||||
float to_cell;
|
||||
int32_t probe_offset[3];
|
||||
uint32_t pad;
|
||||
float pad2[4];
|
||||
};
|
||||
|
||||
//cascade blocks are full-size for volume (128^3), half size for albedo/emission
|
||||
RID sdf_tex;
|
||||
RID light_tex;
|
||||
RID light_aniso_0_tex;
|
||||
RID light_aniso_1_tex;
|
||||
|
||||
RID light_data;
|
||||
RID light_aniso_0_data;
|
||||
RID light_aniso_1_data;
|
||||
|
||||
struct SolidCell { // this struct is unused, but remains as reference for size
|
||||
uint32_t position;
|
||||
uint32_t albedo;
|
||||
uint32_t static_light;
|
||||
uint32_t static_light_aniso;
|
||||
};
|
||||
|
||||
// Buffers for indirect compute dispatch.
|
||||
RID solid_cell_dispatch_buffer_storage;
|
||||
RID solid_cell_dispatch_buffer_call;
|
||||
RID solid_cell_buffer;
|
||||
|
||||
RID lightprobe_history_tex;
|
||||
RID lightprobe_average_tex;
|
||||
|
||||
float cell_size;
|
||||
Vector3i position;
|
||||
|
||||
static const Vector3i DIRTY_ALL;
|
||||
Vector3i dirty_regions; //(0,0,0 is not dirty, negative is refresh from the end, DIRTY_ALL is refresh all.
|
||||
|
||||
RID sdf_store_uniform_set;
|
||||
RID sdf_direct_light_static_uniform_set;
|
||||
RID sdf_direct_light_dynamic_uniform_set;
|
||||
RID scroll_uniform_set;
|
||||
RID scroll_occlusion_uniform_set;
|
||||
RID integrate_uniform_set;
|
||||
RID lights_buffer;
|
||||
|
||||
float baked_exposure_normalization = 1.0;
|
||||
|
||||
bool all_dynamic_lights_dirty = true;
|
||||
};
|
||||
|
||||
// access to our containers
|
||||
GI *gi = nullptr;
|
||||
|
||||
// used for rendering (voxelization)
|
||||
RID render_albedo;
|
||||
RID render_emission;
|
||||
RID render_emission_aniso;
|
||||
RID render_occlusion[8];
|
||||
RID render_geom_facing;
|
||||
|
||||
RID render_sdf[2];
|
||||
RID render_sdf_half[2];
|
||||
|
||||
// used for ping pong processing in cascades
|
||||
RID sdf_initialize_uniform_set;
|
||||
RID sdf_initialize_half_uniform_set;
|
||||
RID jump_flood_uniform_set[2];
|
||||
RID jump_flood_half_uniform_set[2];
|
||||
RID sdf_upscale_uniform_set;
|
||||
int upscale_jfa_uniform_set_index;
|
||||
RID occlusion_uniform_set;
|
||||
|
||||
uint32_t cascade_size = 128;
|
||||
|
||||
LocalVector<Cascade> cascades;
|
||||
|
||||
RID lightprobe_texture;
|
||||
RID lightprobe_data;
|
||||
RID occlusion_texture;
|
||||
RID occlusion_data;
|
||||
RID ambient_texture; //integrates with volumetric fog
|
||||
|
||||
RID lightprobe_history_scroll; //used for scrolling lightprobes
|
||||
RID lightprobe_average_scroll; //used for scrolling lightprobes
|
||||
|
||||
uint32_t history_size = 0;
|
||||
float solid_cell_ratio = 0;
|
||||
uint32_t solid_cell_count = 0;
|
||||
|
||||
int num_cascades = 6;
|
||||
float min_cell_size = 0;
|
||||
uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
|
||||
|
||||
RID debug_uniform_set[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
RID debug_probes_scene_data_ubo;
|
||||
RID debug_probes_uniform_set;
|
||||
RID cascades_ubo;
|
||||
|
||||
bool uses_occlusion = false;
|
||||
float bounce_feedback = 0.5;
|
||||
bool reads_sky = true;
|
||||
float energy = 1.0;
|
||||
float normal_bias = 1.1;
|
||||
float probe_bias = 1.1;
|
||||
RS::EnvironmentSDFGIYScale y_scale_mode = RS::ENV_SDFGI_Y_SCALE_75_PERCENT;
|
||||
|
||||
float y_mult = 1.0;
|
||||
|
||||
uint32_t version = 0;
|
||||
uint32_t render_pass = 0;
|
||||
|
||||
int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically
|
||||
RID integrate_sky_uniform_set;
|
||||
|
||||
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override{};
|
||||
virtual void free_data() override;
|
||||
~SDFGI();
|
||||
|
||||
void create(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi);
|
||||
void update(RID p_env, const Vector3 &p_world_position);
|
||||
void update_light();
|
||||
void update_probes(RID p_env, RendererRD::SkyRD::Sky *p_sky);
|
||||
void store_probes();
|
||||
int get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const;
|
||||
void update_cascades();
|
||||
|
||||
void debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector<RID> &p_texture_views);
|
||||
void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms);
|
||||
|
||||
void pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data);
|
||||
void render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, float p_exposure_normalization);
|
||||
void render_static_lights(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result);
|
||||
};
|
||||
|
||||
RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16;
|
||||
RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_30_FRAMES;
|
||||
RS::EnvironmentSDFGIFramesToUpdateLight sdfgi_frames_to_update_light = RS::ENV_SDFGI_UPDATE_LIGHT_IN_4_FRAMES;
|
||||
|
||||
float sdfgi_solid_cell_ratio = 0.25;
|
||||
Vector3 sdfgi_debug_probe_pos;
|
||||
Vector3 sdfgi_debug_probe_dir;
|
||||
bool sdfgi_debug_probe_enabled = false;
|
||||
Vector3i sdfgi_debug_probe_index;
|
||||
uint32_t sdfgi_current_version = 0;
|
||||
|
||||
/* SDFGI UPDATE */
|
||||
|
||||
int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; }
|
||||
|
||||
virtual void sdfgi_reset() override;
|
||||
|
||||
struct SDFGIData {
|
||||
float grid_size[3];
|
||||
uint32_t max_cascades;
|
||||
|
||||
uint32_t use_occlusion;
|
||||
int32_t probe_axis_size;
|
||||
float probe_to_uvw;
|
||||
float normal_bias;
|
||||
|
||||
float lightprobe_tex_pixel_size[3];
|
||||
float energy;
|
||||
|
||||
float lightprobe_uv_offset[3];
|
||||
float y_mult;
|
||||
|
||||
float occlusion_clamp[3];
|
||||
uint32_t pad3;
|
||||
|
||||
float occlusion_renormalize[3];
|
||||
uint32_t pad4;
|
||||
|
||||
float cascade_probe_size[3];
|
||||
uint32_t pad5;
|
||||
|
||||
struct ProbeCascadeData {
|
||||
float position[3]; //offset of (0,0,0) in world coordinates
|
||||
float to_probe; // 1/bounds * grid_size
|
||||
int32_t probe_world_offset[3];
|
||||
float to_cell; // 1/bounds * grid_size
|
||||
float pad[3];
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
ProbeCascadeData cascades[SDFGI::MAX_CASCADES];
|
||||
};
|
||||
|
||||
struct VoxelGIData {
|
||||
float xform[16]; // 64 - 64
|
||||
|
||||
float bounds[3]; // 12 - 76
|
||||
float dynamic_range; // 4 - 80
|
||||
|
||||
float bias; // 4 - 84
|
||||
float normal_bias; // 4 - 88
|
||||
uint32_t blend_ambient; // 4 - 92
|
||||
uint32_t mipmaps; // 4 - 96
|
||||
|
||||
float pad[3]; // 12 - 108
|
||||
float exposure_normalization; // 4 - 112
|
||||
};
|
||||
|
||||
struct SceneData {
|
||||
float inv_projection[2][16];
|
||||
float cam_transform[16];
|
||||
float eye_offset[2][4];
|
||||
|
||||
int32_t screen_size[2];
|
||||
float pad1;
|
||||
float pad2;
|
||||
};
|
||||
|
||||
struct PushConstant {
|
||||
uint32_t max_voxel_gi_instances;
|
||||
uint32_t high_quality_vct;
|
||||
uint32_t orthogonal;
|
||||
uint32_t view_index;
|
||||
|
||||
float proj_info[4];
|
||||
|
||||
float z_near;
|
||||
float z_far;
|
||||
float pad2;
|
||||
float pad3;
|
||||
};
|
||||
|
||||
RID sdfgi_ubo;
|
||||
|
||||
enum Mode {
|
||||
MODE_VOXEL_GI,
|
||||
MODE_SDFGI,
|
||||
MODE_COMBINED,
|
||||
MODE_MAX
|
||||
};
|
||||
|
||||
enum ShaderSpecializations {
|
||||
SHADER_SPECIALIZATION_HALF_RES = 1 << 0,
|
||||
SHADER_SPECIALIZATION_USE_FULL_PROJECTION_MATRIX = 1 << 1,
|
||||
SHADER_SPECIALIZATION_USE_VRS = 1 << 2,
|
||||
SHADER_SPECIALIZATION_VARIATIONS = 8,
|
||||
};
|
||||
|
||||
RID default_voxel_gi_buffer;
|
||||
|
||||
bool half_resolution = false;
|
||||
GiShaderRD shader;
|
||||
RID shader_version;
|
||||
RID pipelines[SHADER_SPECIALIZATION_VARIATIONS][MODE_MAX];
|
||||
|
||||
GI();
|
||||
~GI();
|
||||
|
||||
void init(RendererRD::SkyRD *p_sky);
|
||||
void free();
|
||||
|
||||
Ref<SDFGI> create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size);
|
||||
|
||||
void setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBuffersRD> p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used);
|
||||
void process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances);
|
||||
|
||||
RID voxel_gi_instance_create(RID p_base);
|
||||
void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform);
|
||||
bool voxel_gi_needs_update(RID p_probe) const;
|
||||
void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects);
|
||||
void debug_voxel_gi(RID p_voxel_gi, RD::DrawListID p_draw_list, RID p_framebuffer, const Projection &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
#endif // GI_RD_H
|
||||
1728
engine/servers/rendering/renderer_rd/environment/sky.cpp
Normal file
1728
engine/servers/rendering/renderer_rd/environment/sky.cpp
Normal file
File diff suppressed because it is too large
Load diff
321
engine/servers/rendering/renderer_rd/environment/sky.h
Normal file
321
engine/servers/rendering/renderer_rd/environment/sky.h
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/**************************************************************************/
|
||||
/* sky.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SKY_RD_H
|
||||
#define SKY_RD_H
|
||||
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/environment/sky.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/shader_compiler.h"
|
||||
|
||||
// Forward declare RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound
|
||||
class RendererSceneRenderRD;
|
||||
class RenderSceneBuffersRD;
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
class SkyRD {
|
||||
public:
|
||||
enum SkySet {
|
||||
SKY_SET_UNIFORMS,
|
||||
SKY_SET_MATERIAL,
|
||||
SKY_SET_TEXTURES,
|
||||
SKY_SET_FOG,
|
||||
};
|
||||
|
||||
const int SAMPLERS_BINDING_FIRST_INDEX = 4;
|
||||
|
||||
// Skys need less info from Directional Lights than the normal shaders
|
||||
struct SkyDirectionalLightData {
|
||||
float direction[3];
|
||||
float energy;
|
||||
float color[3];
|
||||
float size;
|
||||
uint32_t enabled;
|
||||
uint32_t pad[3];
|
||||
};
|
||||
|
||||
private:
|
||||
RD::DataFormat texture_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
||||
|
||||
enum SkyTextureSetVersion {
|
||||
SKY_TEXTURE_SET_BACKGROUND,
|
||||
SKY_TEXTURE_SET_HALF_RES,
|
||||
SKY_TEXTURE_SET_QUARTER_RES,
|
||||
SKY_TEXTURE_SET_CUBEMAP,
|
||||
SKY_TEXTURE_SET_CUBEMAP_HALF_RES,
|
||||
SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES,
|
||||
SKY_TEXTURE_SET_MAX
|
||||
};
|
||||
|
||||
enum SkyVersion {
|
||||
SKY_VERSION_BACKGROUND,
|
||||
SKY_VERSION_HALF_RES,
|
||||
SKY_VERSION_QUARTER_RES,
|
||||
SKY_VERSION_CUBEMAP,
|
||||
SKY_VERSION_CUBEMAP_HALF_RES,
|
||||
SKY_VERSION_CUBEMAP_QUARTER_RES,
|
||||
|
||||
SKY_VERSION_BACKGROUND_MULTIVIEW,
|
||||
SKY_VERSION_HALF_RES_MULTIVIEW,
|
||||
SKY_VERSION_QUARTER_RES_MULTIVIEW,
|
||||
|
||||
SKY_VERSION_MAX
|
||||
};
|
||||
|
||||
struct SkyPushConstant {
|
||||
float orientation[12]; // 48 - 48
|
||||
float projection[4]; // 16 - 64
|
||||
float position[3]; // 12 - 76
|
||||
float time; // 4 - 80
|
||||
float pad[3]; // 12 - 92
|
||||
float luminance_multiplier; // 4 - 96
|
||||
// 128 is the max size of a push constant. We can replace "pad" but we can't add any more.
|
||||
};
|
||||
|
||||
struct SkyShaderData : public RendererRD::MaterialStorage::ShaderData {
|
||||
bool valid = false;
|
||||
RID version;
|
||||
|
||||
PipelineCacheRD pipelines[SKY_VERSION_MAX];
|
||||
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size = 0;
|
||||
|
||||
String code;
|
||||
|
||||
bool uses_time = false;
|
||||
bool uses_position = false;
|
||||
bool uses_half_res = false;
|
||||
bool uses_quarter_res = false;
|
||||
bool uses_light = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
|
||||
|
||||
SkyShaderData() {}
|
||||
virtual ~SkyShaderData();
|
||||
};
|
||||
|
||||
void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const Projection &p_projection, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier);
|
||||
|
||||
public:
|
||||
struct SkySceneState {
|
||||
struct UBO {
|
||||
float combined_reprojection[RendererSceneRender::MAX_RENDER_VIEWS][16]; // 2 x 64 - 128
|
||||
float view_inv_projections[RendererSceneRender::MAX_RENDER_VIEWS][16]; // 2 x 64 - 256
|
||||
float view_eye_offsets[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 288
|
||||
|
||||
uint32_t volumetric_fog_enabled; // 4 - 292
|
||||
float volumetric_fog_inv_length; // 4 - 296
|
||||
float volumetric_fog_detail_spread; // 4 - 300
|
||||
float volumetric_fog_sky_affect; // 4 - 304
|
||||
|
||||
uint32_t fog_enabled; // 4 - 308
|
||||
float fog_sky_affect; // 4 - 312
|
||||
float fog_density; // 4 - 316
|
||||
float fog_sun_scatter; // 4 - 320
|
||||
|
||||
float fog_light_color[3]; // 12 - 332
|
||||
float fog_aerial_perspective; // 4 - 336
|
||||
|
||||
float z_far; // 4 - 340
|
||||
uint32_t directional_light_count; // 4 - 344
|
||||
uint32_t pad1; // 4 - 348
|
||||
uint32_t pad2; // 4 - 352
|
||||
};
|
||||
|
||||
UBO ubo;
|
||||
|
||||
uint32_t view_count = 1;
|
||||
Transform3D cam_transform;
|
||||
Projection cam_projection;
|
||||
|
||||
SkyDirectionalLightData *directional_lights = nullptr;
|
||||
SkyDirectionalLightData *last_frame_directional_lights = nullptr;
|
||||
uint32_t max_directional_lights;
|
||||
uint32_t last_frame_directional_light_count;
|
||||
RID directional_light_buffer;
|
||||
RID uniform_set;
|
||||
RID uniform_buffer;
|
||||
RID fog_uniform_set;
|
||||
RID default_fog_uniform_set;
|
||||
|
||||
RID fog_shader;
|
||||
RID fog_material;
|
||||
RID fog_only_texture_uniform_set;
|
||||
} sky_scene_state;
|
||||
|
||||
struct ReflectionData {
|
||||
struct Layer {
|
||||
struct Mipmap {
|
||||
RID framebuffers[6];
|
||||
RID views[6];
|
||||
Size2i size;
|
||||
};
|
||||
Vector<Mipmap> mipmaps; //per-face view
|
||||
Vector<RID> views; // per-cubemap view
|
||||
};
|
||||
|
||||
struct DownsampleLayer {
|
||||
struct Mipmap {
|
||||
RID view;
|
||||
Size2i size;
|
||||
|
||||
// for mobile only
|
||||
RID views[6];
|
||||
RID framebuffers[6];
|
||||
};
|
||||
Vector<Mipmap> mipmaps;
|
||||
};
|
||||
|
||||
RID radiance_base_cubemap; //cubemap for first layer, first cubemap
|
||||
RID downsampled_radiance_cubemap;
|
||||
DownsampleLayer downsampled_layer;
|
||||
RID coefficient_buffer;
|
||||
|
||||
bool dirty = true;
|
||||
|
||||
Vector<Layer> layers;
|
||||
|
||||
void clear_reflection_data();
|
||||
void update_reflection_data(int p_size, int p_mipmaps, bool p_use_array, RID p_base_cube, int p_base_layer, bool p_low_quality, int p_roughness_layers, RD::DataFormat p_texture_format);
|
||||
void create_reflection_fast_filter(bool p_use_arrays);
|
||||
void create_reflection_importance_sample(bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality);
|
||||
void update_reflection_mipmaps(int p_start, int p_end);
|
||||
};
|
||||
|
||||
/* Sky shader */
|
||||
|
||||
struct SkyShader {
|
||||
SkyShaderRD shader;
|
||||
ShaderCompiler compiler;
|
||||
|
||||
RID default_shader;
|
||||
RID default_material;
|
||||
RID default_shader_rd;
|
||||
} sky_shader;
|
||||
|
||||
struct SkyMaterialData : public RendererRD::MaterialStorage::MaterialData {
|
||||
SkyShaderData *shader_data = nullptr;
|
||||
RID uniform_set;
|
||||
bool uniform_set_updated;
|
||||
|
||||
virtual void set_render_priority(int p_priority) {}
|
||||
virtual void set_next_pass(RID p_pass) {}
|
||||
virtual bool update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~SkyMaterialData();
|
||||
};
|
||||
|
||||
struct Sky {
|
||||
RID radiance;
|
||||
RID quarter_res_pass;
|
||||
RID quarter_res_framebuffer;
|
||||
Size2i screen_size;
|
||||
|
||||
RID uniform_set;
|
||||
|
||||
RID material;
|
||||
RID uniform_buffer;
|
||||
|
||||
int radiance_size = 256;
|
||||
|
||||
RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC;
|
||||
|
||||
ReflectionData reflection;
|
||||
bool dirty = false;
|
||||
int processing_layer = 0;
|
||||
Sky *dirty_list = nullptr;
|
||||
float baked_exposure = 1.0;
|
||||
|
||||
//State to track when radiance cubemap needs updating
|
||||
SkyMaterialData *prev_material = nullptr;
|
||||
Vector3 prev_position;
|
||||
float prev_time;
|
||||
|
||||
void free();
|
||||
|
||||
RID get_textures(SkyTextureSetVersion p_version, RID p_default_shader_rd, Ref<RenderSceneBuffersRD> p_render_buffers);
|
||||
bool set_radiance_size(int p_radiance_size);
|
||||
bool set_mode(RS::SkyMode p_mode);
|
||||
bool set_material(RID p_material);
|
||||
Ref<Image> bake_panorama(float p_energy, int p_roughness_layers, const Size2i &p_size);
|
||||
};
|
||||
|
||||
uint32_t sky_ggx_samples_quality;
|
||||
bool sky_use_cubemap_array;
|
||||
Sky *dirty_sky_list = nullptr;
|
||||
mutable RID_Owner<Sky, true> sky_owner;
|
||||
int roughness_layers;
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *_create_sky_shader_func();
|
||||
static RendererRD::MaterialStorage::ShaderData *_create_sky_shader_funcs();
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *_create_sky_material_func(SkyShaderData *p_shader);
|
||||
static RendererRD::MaterialStorage::MaterialData *_create_sky_material_funcs(RendererRD::MaterialStorage::ShaderData *p_shader);
|
||||
|
||||
SkyRD();
|
||||
void init();
|
||||
void set_texture_format(RD::DataFormat p_texture_format);
|
||||
~SkyRD();
|
||||
|
||||
void setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, uint32_t p_view_count, const Projection *p_view_projections, const Vector3 *p_view_eye_offsets, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Size2i p_screen_size, Vector2 p_jitter, RendererSceneRenderRD *p_scene_render);
|
||||
void update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_env, const Vector3 &p_global_pos, double p_time, float p_luminance_multiplier = 1.0);
|
||||
void update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_env, double p_time, float p_luminance_multiplier = 1.0);
|
||||
void draw_sky(RD::DrawListID p_draw_list, Ref<RenderSceneBuffersRD> p_render_buffers, RID p_env, RID p_fb, double p_time, float p_luminance_multiplier = 1.0);
|
||||
|
||||
void invalidate_sky(Sky *p_sky);
|
||||
void update_dirty_skys();
|
||||
|
||||
RID sky_get_material(RID p_sky) const;
|
||||
RID sky_get_radiance_texture_rd(RID p_sky) const;
|
||||
float sky_get_baked_exposure(RID p_sky) const;
|
||||
|
||||
RID allocate_sky_rid();
|
||||
void initialize_sky_rid(RID p_rid);
|
||||
Sky *get_sky(RID p_sky) const;
|
||||
void free_sky(RID p_sky);
|
||||
void sky_set_radiance_size(RID p_sky, int p_radiance_size);
|
||||
void sky_set_mode(RID p_sky, RS::SkyMode p_mode);
|
||||
void sky_set_material(RID p_sky, RID p_material);
|
||||
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size);
|
||||
};
|
||||
|
||||
} // namespace RendererRD
|
||||
|
||||
#endif // SKY_RD_H
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,671 @@
|
|||
/**************************************************************************/
|
||||
/* render_forward_clustered.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDER_FORWARD_CLUSTERED_H
|
||||
#define RENDER_FORWARD_CLUSTERED_H
|
||||
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "servers/rendering/renderer_rd/cluster_builder_rd.h"
|
||||
#include "servers/rendering/renderer_rd/effects/fsr2.h"
|
||||
#include "servers/rendering/renderer_rd/effects/resolve.h"
|
||||
#include "servers/rendering/renderer_rd/effects/ss_effects.h"
|
||||
#include "servers/rendering/renderer_rd/effects/taa.h"
|
||||
#include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h"
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/forward_clustered/best_fit_normal.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/utilities.h"
|
||||
|
||||
#define RB_SCOPE_FORWARD_CLUSTERED SNAME("forward_clustered")
|
||||
|
||||
#define RB_TEX_SPECULAR SNAME("specular")
|
||||
#define RB_TEX_SPECULAR_MSAA SNAME("specular_msaa")
|
||||
#define RB_TEX_NORMAL_ROUGHNESS SNAME("normal_roughness")
|
||||
#define RB_TEX_NORMAL_ROUGHNESS_MSAA SNAME("normal_roughness_msaa")
|
||||
#define RB_TEX_VOXEL_GI SNAME("voxel_gi")
|
||||
#define RB_TEX_VOXEL_GI_MSAA SNAME("voxel_gi_msaa")
|
||||
|
||||
namespace RendererSceneRenderImplementation {
|
||||
|
||||
class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
friend SceneShaderForwardClustered;
|
||||
|
||||
enum {
|
||||
SCENE_UNIFORM_SET = 0,
|
||||
RENDER_PASS_UNIFORM_SET = 1,
|
||||
TRANSFORMS_UNIFORM_SET = 2,
|
||||
MATERIAL_UNIFORM_SET = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6,
|
||||
SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7,
|
||||
SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 8,
|
||||
SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 9,
|
||||
SPEC_CONSTANT_DECAL_FILTER = 10,
|
||||
SPEC_CONSTANT_PROJECTOR_FILTER = 11,
|
||||
SPEC_CONSTANT_USE_DEPTH_FOG = 12,
|
||||
};
|
||||
|
||||
enum {
|
||||
SDFGI_MAX_CASCADES = 8,
|
||||
MAX_VOXEL_GI_INSTANCESS = 8,
|
||||
MAX_LIGHTMAPS = 8,
|
||||
MAX_VOXEL_GI_INSTANCESS_PER_INSTANCE = 2,
|
||||
INSTANCE_DATA_BUFFER_MIN_SIZE = 4096
|
||||
};
|
||||
|
||||
enum RenderListType {
|
||||
RENDER_LIST_OPAQUE, //used for opaque objects
|
||||
RENDER_LIST_MOTION, //used for opaque objects with motion
|
||||
RENDER_LIST_ALPHA, //used for transparent objects
|
||||
RENDER_LIST_SECONDARY, //used for shadows and other objects
|
||||
RENDER_LIST_MAX
|
||||
};
|
||||
|
||||
/* Scene Shader */
|
||||
|
||||
SceneShaderForwardClustered scene_shader;
|
||||
|
||||
/* Framebuffer */
|
||||
|
||||
class RenderBufferDataForwardClustered : public RenderBufferCustomDataRD {
|
||||
GDCLASS(RenderBufferDataForwardClustered, RenderBufferCustomDataRD)
|
||||
|
||||
private:
|
||||
RenderSceneBuffersRD *render_buffers = nullptr;
|
||||
RendererRD::FSR2Context *fsr2_context = nullptr;
|
||||
|
||||
public:
|
||||
ClusterBuilderRD *cluster_builder = nullptr;
|
||||
|
||||
struct SSEffectsData {
|
||||
Projection last_frame_projections[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
Transform3D last_frame_transform;
|
||||
|
||||
RendererRD::SSEffects::SSILRenderBuffers ssil;
|
||||
RendererRD::SSEffects::SSAORenderBuffers ssao;
|
||||
RendererRD::SSEffects::SSRRenderBuffers ssr;
|
||||
} ss_effects_data;
|
||||
|
||||
enum DepthFrameBufferType {
|
||||
DEPTH_FB,
|
||||
DEPTH_FB_ROUGHNESS,
|
||||
DEPTH_FB_ROUGHNESS_VOXELGI
|
||||
};
|
||||
|
||||
RID render_sdfgi_uniform_set;
|
||||
|
||||
void ensure_specular();
|
||||
bool has_specular() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); }
|
||||
RID get_specular() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); }
|
||||
RID get_specular(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, p_layer, 0); }
|
||||
RID get_specular_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, p_layer, 0); }
|
||||
|
||||
void ensure_normal_roughness_texture();
|
||||
bool has_normal_roughness() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS); }
|
||||
RID get_normal_roughness() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS); }
|
||||
RID get_normal_roughness(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS, p_layer, 0); }
|
||||
RID get_normal_roughness_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS_MSAA); }
|
||||
RID get_normal_roughness_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS_MSAA, p_layer, 0); }
|
||||
|
||||
void ensure_voxelgi();
|
||||
bool has_voxelgi() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI); }
|
||||
RID get_voxelgi() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI); }
|
||||
RID get_voxelgi(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, p_layer, 0); }
|
||||
RID get_voxelgi_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, p_layer, 0); }
|
||||
|
||||
void ensure_fsr2(RendererRD::FSR2Effect *p_effect);
|
||||
RendererRD::FSR2Context *get_fsr2_context() const { return fsr2_context; }
|
||||
|
||||
RID get_color_only_fb();
|
||||
RID get_color_pass_fb(uint32_t p_color_pass_flags);
|
||||
RID get_depth_fb(DepthFrameBufferType p_type = DEPTH_FB);
|
||||
RID get_specular_only_fb();
|
||||
RID get_velocity_only_fb();
|
||||
|
||||
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
|
||||
virtual void free_data() override;
|
||||
};
|
||||
|
||||
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
|
||||
RID render_base_uniform_set;
|
||||
|
||||
uint64_t lightmap_texture_array_version = 0xFFFFFFFF;
|
||||
|
||||
void _update_render_base_uniform_set();
|
||||
RID _setup_sdfgi_render_pass_uniform_set(RID p_albedo_texture, RID p_emission_texture, RID p_emission_aniso_texture, RID p_geom_facing_texture, const RendererRD::MaterialStorage::Samplers &p_samplers);
|
||||
RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0);
|
||||
|
||||
struct BestFitNormal {
|
||||
BestFitNormalShaderRD shader;
|
||||
RID shader_version;
|
||||
RID pipeline;
|
||||
RID texture;
|
||||
} best_fit_normal;
|
||||
|
||||
enum PassMode {
|
||||
PASS_MODE_COLOR,
|
||||
PASS_MODE_SHADOW,
|
||||
PASS_MODE_SHADOW_DP,
|
||||
PASS_MODE_DEPTH,
|
||||
PASS_MODE_DEPTH_NORMAL_ROUGHNESS,
|
||||
PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI,
|
||||
PASS_MODE_DEPTH_MATERIAL,
|
||||
PASS_MODE_SDF,
|
||||
};
|
||||
|
||||
enum ColorPassFlags {
|
||||
COLOR_PASS_FLAG_TRANSPARENT = 1 << 0,
|
||||
COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1,
|
||||
COLOR_PASS_FLAG_MULTIVIEW = 1 << 2,
|
||||
COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 3,
|
||||
};
|
||||
|
||||
struct GeometryInstanceSurfaceDataCache;
|
||||
struct RenderElementInfo;
|
||||
|
||||
struct RenderListParameters {
|
||||
GeometryInstanceSurfaceDataCache **elements = nullptr;
|
||||
RenderElementInfo *element_info = nullptr;
|
||||
int element_count = 0;
|
||||
bool reverse_cull = false;
|
||||
PassMode pass_mode = PASS_MODE_COLOR;
|
||||
uint32_t color_pass_flags = 0;
|
||||
bool no_gi = false;
|
||||
uint32_t view_count = 1;
|
||||
RID render_pass_uniform_set;
|
||||
bool force_wireframe = false;
|
||||
Vector2 uv_offset;
|
||||
float lod_distance_multiplier = 0.0;
|
||||
float screen_mesh_lod_threshold = 0.0;
|
||||
RD::FramebufferFormatID framebuffer_format = 0;
|
||||
uint32_t element_offset = 0;
|
||||
bool use_directional_soft_shadow = false;
|
||||
uint32_t spec_constant_base_flags = 0;
|
||||
|
||||
RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_spec_constant_base_flags = 0) {
|
||||
elements = p_elements;
|
||||
element_info = p_element_info;
|
||||
element_count = p_element_count;
|
||||
reverse_cull = p_reverse_cull;
|
||||
pass_mode = p_pass_mode;
|
||||
color_pass_flags = p_color_pass_flags;
|
||||
no_gi = p_no_gi;
|
||||
view_count = p_view_count;
|
||||
render_pass_uniform_set = p_render_pass_uniform_set;
|
||||
force_wireframe = p_force_wireframe;
|
||||
uv_offset = p_uv_offset;
|
||||
lod_distance_multiplier = p_lod_distance_multiplier;
|
||||
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
|
||||
element_offset = p_element_offset;
|
||||
use_directional_soft_shadow = p_use_directional_soft_shadows;
|
||||
spec_constant_base_flags = p_spec_constant_base_flags;
|
||||
}
|
||||
};
|
||||
|
||||
struct LightmapData {
|
||||
float normal_xform[12];
|
||||
float pad[3];
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
struct LightmapCaptureData {
|
||||
float sh[9 * 4];
|
||||
};
|
||||
|
||||
// When changing any of these enums, remember to change the corresponding enums in the shader files as well.
|
||||
enum {
|
||||
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
|
||||
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
|
||||
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
|
||||
INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8,
|
||||
INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9,
|
||||
INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 10,
|
||||
INSTANCE_DATA_FLAG_PARTICLES = 1 << 11,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
|
||||
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT = 16,
|
||||
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_MASK = 0xFF,
|
||||
INSTANCE_DATA_FLAGS_FADE_SHIFT = 24,
|
||||
INSTANCE_DATA_FLAGS_FADE_MASK = 0xFFUL << INSTANCE_DATA_FLAGS_FADE_SHIFT
|
||||
};
|
||||
|
||||
struct SceneState {
|
||||
// This struct is loaded into Set 1 - Binding 1, populated at start of rendering a frame, must match with shader code
|
||||
struct UBO {
|
||||
uint32_t cluster_shift;
|
||||
uint32_t cluster_width;
|
||||
uint32_t cluster_type_size;
|
||||
uint32_t max_cluster_element_count_div_32;
|
||||
|
||||
uint32_t ss_effects_flags;
|
||||
float ssao_light_affect;
|
||||
float ssao_ao_affect;
|
||||
uint32_t pad1;
|
||||
|
||||
float sdf_to_bounds[16];
|
||||
|
||||
int32_t sdf_offset[3];
|
||||
uint32_t pad2;
|
||||
|
||||
int32_t sdf_size[3];
|
||||
uint32_t gi_upscale_for_msaa;
|
||||
|
||||
uint32_t volumetric_fog_enabled;
|
||||
float volumetric_fog_inv_length;
|
||||
float volumetric_fog_detail_spread;
|
||||
uint32_t volumetric_fog_pad;
|
||||
};
|
||||
|
||||
struct PushConstant {
|
||||
uint32_t base_index; //
|
||||
uint32_t uv_offset; //packed
|
||||
uint32_t multimesh_motion_vectors_current_offset;
|
||||
uint32_t multimesh_motion_vectors_previous_offset;
|
||||
};
|
||||
|
||||
struct InstanceData {
|
||||
float transform[16];
|
||||
float prev_transform[16];
|
||||
uint32_t flags;
|
||||
uint32_t instance_uniforms_ofs; //base offset in global buffer for instance variables
|
||||
uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap index)
|
||||
uint32_t layer_mask;
|
||||
float lightmap_uv_scale[4];
|
||||
float compressed_aabb_position[4];
|
||||
float compressed_aabb_size[4];
|
||||
float uv_scale[4];
|
||||
};
|
||||
|
||||
UBO ubo;
|
||||
|
||||
LocalVector<RID> uniform_buffers;
|
||||
LocalVector<RID> implementation_uniform_buffers;
|
||||
|
||||
LightmapData lightmaps[MAX_LIGHTMAPS];
|
||||
RID lightmap_ids[MAX_LIGHTMAPS];
|
||||
bool lightmap_has_sh[MAX_LIGHTMAPS];
|
||||
uint32_t lightmaps_used = 0;
|
||||
uint32_t max_lightmaps;
|
||||
RID lightmap_buffer;
|
||||
|
||||
RID instance_buffer[RENDER_LIST_MAX];
|
||||
uint32_t instance_buffer_size[RENDER_LIST_MAX] = { 0, 0, 0 };
|
||||
LocalVector<InstanceData> instance_data[RENDER_LIST_MAX];
|
||||
|
||||
LightmapCaptureData *lightmap_captures = nullptr;
|
||||
uint32_t max_lightmap_captures;
|
||||
RID lightmap_capture_buffer;
|
||||
|
||||
RID voxelgi_ids[MAX_VOXEL_GI_INSTANCESS];
|
||||
uint32_t voxelgis_used = 0;
|
||||
|
||||
bool used_screen_texture = false;
|
||||
bool used_normal_texture = false;
|
||||
bool used_depth_texture = false;
|
||||
bool used_sss = false;
|
||||
bool used_lightmap = false;
|
||||
|
||||
struct ShadowPass {
|
||||
uint32_t element_from;
|
||||
uint32_t element_count;
|
||||
bool flip_cull;
|
||||
PassMode pass_mode;
|
||||
|
||||
RID rp_uniform_set;
|
||||
float lod_distance_multiplier;
|
||||
float screen_mesh_lod_threshold;
|
||||
|
||||
RID framebuffer;
|
||||
RD::InitialAction initial_depth_action;
|
||||
Rect2i rect;
|
||||
};
|
||||
|
||||
LocalVector<ShadowPass> shadow_passes;
|
||||
|
||||
} scene_state;
|
||||
|
||||
static RenderForwardClustered *singleton;
|
||||
|
||||
void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_apply_alpha_multiplier = false, bool p_pancake_shadows = false, int p_index = 0);
|
||||
void _setup_voxelgis(const PagedArray<RID> &p_voxelgis);
|
||||
void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform);
|
||||
|
||||
struct RenderElementInfo {
|
||||
enum { MAX_REPEATS = (1 << 20) - 1 };
|
||||
uint32_t repeat : 20;
|
||||
uint32_t uses_projector : 1;
|
||||
uint32_t uses_softshadow : 1;
|
||||
uint32_t uses_lightmap : 1;
|
||||
uint32_t uses_forward_gi : 1;
|
||||
uint32_t lod_index : 8;
|
||||
};
|
||||
|
||||
template <PassMode p_pass_mode, uint32_t p_color_pass_flags = 0>
|
||||
_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
|
||||
|
||||
void _update_instance_data_buffer(RenderListType p_render_list);
|
||||
void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
|
||||
void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi = false, bool p_using_opaque_gi = false, bool p_using_motion_pass = false, bool p_append = false);
|
||||
|
||||
HashMap<Size2i, RID> sdfgi_framebuffer_size_cache;
|
||||
|
||||
struct GeometryInstanceData;
|
||||
class GeometryInstanceForwardClustered;
|
||||
|
||||
struct GeometryInstanceLightmapSH {
|
||||
Color sh[9];
|
||||
};
|
||||
|
||||
// Cached data for drawing surfaces
|
||||
struct GeometryInstanceSurfaceDataCache {
|
||||
enum {
|
||||
FLAG_PASS_DEPTH = 1,
|
||||
FLAG_PASS_OPAQUE = 2,
|
||||
FLAG_PASS_ALPHA = 4,
|
||||
FLAG_PASS_SHADOW = 8,
|
||||
FLAG_USES_SHARED_SHADOW_MATERIAL = 128,
|
||||
FLAG_USES_SUBSURFACE_SCATTERING = 2048,
|
||||
FLAG_USES_SCREEN_TEXTURE = 4096,
|
||||
FLAG_USES_DEPTH_TEXTURE = 8192,
|
||||
FLAG_USES_NORMAL_TEXTURE = 16384,
|
||||
FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768,
|
||||
FLAG_USES_PARTICLE_TRAILS = 65536,
|
||||
FLAG_USES_MOTION_VECTOR = 131072,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint64_t lod_index : 8;
|
||||
uint64_t surface_index : 8;
|
||||
uint64_t geometry_id : 32;
|
||||
uint64_t material_id_low : 16;
|
||||
|
||||
uint64_t material_id_hi : 16;
|
||||
uint64_t shader_id : 32;
|
||||
uint64_t uses_softshadow : 1;
|
||||
uint64_t uses_projector : 1;
|
||||
uint64_t uses_forward_gi : 1;
|
||||
uint64_t uses_lightmap : 1;
|
||||
uint64_t depth_layer : 4;
|
||||
uint64_t priority : 8;
|
||||
};
|
||||
struct {
|
||||
uint64_t sort_key1;
|
||||
uint64_t sort_key2;
|
||||
};
|
||||
} sort;
|
||||
|
||||
RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
|
||||
uint32_t flags = 0;
|
||||
uint32_t surface_index = 0;
|
||||
uint32_t color_pass_inclusion_mask = 0;
|
||||
|
||||
void *surface = nullptr;
|
||||
RID material_uniform_set;
|
||||
SceneShaderForwardClustered::ShaderData *shader = nullptr;
|
||||
SceneShaderForwardClustered::MaterialData *material = nullptr;
|
||||
|
||||
void *surface_shadow = nullptr;
|
||||
RID material_uniform_set_shadow;
|
||||
SceneShaderForwardClustered::ShaderData *shader_shadow = nullptr;
|
||||
|
||||
GeometryInstanceSurfaceDataCache *next = nullptr;
|
||||
GeometryInstanceForwardClustered *owner = nullptr;
|
||||
};
|
||||
|
||||
class GeometryInstanceForwardClustered : public RenderGeometryInstanceBase {
|
||||
public:
|
||||
// lightmap
|
||||
RID lightmap_instance;
|
||||
Rect2 lightmap_uv_scale;
|
||||
uint32_t lightmap_slice_index;
|
||||
GeometryInstanceLightmapSH *lightmap_sh = nullptr;
|
||||
|
||||
//used during rendering
|
||||
|
||||
uint32_t gi_offset_cache = 0;
|
||||
bool store_transform_cache = true;
|
||||
RID transforms_uniform_set;
|
||||
uint32_t instance_count = 0;
|
||||
uint32_t trail_steps = 1;
|
||||
bool can_sdfgi = false;
|
||||
bool using_projectors = false;
|
||||
bool using_softshadows = false;
|
||||
|
||||
//used during setup
|
||||
uint64_t prev_transform_change_frame = 0xFFFFFFFF;
|
||||
bool prev_transform_dirty = true;
|
||||
Transform3D prev_transform;
|
||||
RID voxel_gi_instances[MAX_VOXEL_GI_INSTANCESS_PER_INSTANCE];
|
||||
GeometryInstanceSurfaceDataCache *surface_caches = nullptr;
|
||||
SelfList<GeometryInstanceForwardClustered> dirty_list_element;
|
||||
|
||||
GeometryInstanceForwardClustered() :
|
||||
dirty_list_element(this) {}
|
||||
|
||||
virtual void _mark_dirty() override;
|
||||
|
||||
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) override;
|
||||
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
|
||||
virtual void set_lightmap_capture(const Color *p_sh9) override;
|
||||
|
||||
virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override {}
|
||||
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {}
|
||||
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
|
||||
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override;
|
||||
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override;
|
||||
};
|
||||
|
||||
static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
|
||||
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
|
||||
|
||||
SelfList<GeometryInstanceForwardClustered>::List geometry_instance_dirty_list;
|
||||
|
||||
PagedAllocator<GeometryInstanceForwardClustered> geometry_instance_alloc;
|
||||
PagedAllocator<GeometryInstanceSurfaceDataCache> geometry_instance_surface_alloc;
|
||||
PagedAllocator<GeometryInstanceLightmapSH> geometry_instance_lightmap_sh;
|
||||
|
||||
void _geometry_instance_add_surface_with_material(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
|
||||
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh);
|
||||
void _geometry_instance_add_surface(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
|
||||
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
|
||||
void _update_dirty_geometry_instances();
|
||||
|
||||
/* Render List */
|
||||
|
||||
struct RenderList {
|
||||
LocalVector<GeometryInstanceSurfaceDataCache *> elements;
|
||||
LocalVector<RenderElementInfo> element_info;
|
||||
|
||||
void clear() {
|
||||
elements.clear();
|
||||
element_info.clear();
|
||||
}
|
||||
|
||||
//should eventually be replaced by radix
|
||||
|
||||
struct SortByKey {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_key() {
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
void sort_by_key_range(uint32_t p_from, uint32_t p_size) {
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr() + p_from, p_size);
|
||||
}
|
||||
|
||||
struct SortByDepth {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->owner->depth < B->owner->depth);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_depth() { //used for shadows
|
||||
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByDepth> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
struct SortByReverseDepthAndPriority {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_reverse_depth_and_priority() { //used for alpha
|
||||
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByReverseDepthAndPriority> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_element(GeometryInstanceSurfaceDataCache *p_element) {
|
||||
elements.push_back(p_element);
|
||||
}
|
||||
};
|
||||
|
||||
RenderList render_list[RENDER_LIST_MAX];
|
||||
|
||||
virtual void _update_shader_quality_settings() override;
|
||||
|
||||
/* Effects */
|
||||
|
||||
RendererRD::Resolve *resolve_effects = nullptr;
|
||||
RendererRD::TAA *taa = nullptr;
|
||||
RendererRD::FSR2Effect *fsr2_effect = nullptr;
|
||||
RendererRD::SSEffects *ss_effects = nullptr;
|
||||
|
||||
/* Cluster builder */
|
||||
|
||||
ClusterBuilderSharedDataRD cluster_builder_shared;
|
||||
ClusterBuilderRD *current_cluster_builder = nullptr;
|
||||
|
||||
/* SDFGI */
|
||||
void _update_sdfgi(RenderDataRD *p_render_data);
|
||||
|
||||
/* Volumetric fog */
|
||||
RID shadow_sampler;
|
||||
|
||||
void _update_volumetric_fog(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray<RID> &p_fog_volumes);
|
||||
|
||||
/* Render shadows */
|
||||
|
||||
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
|
||||
void _render_shadow_begin();
|
||||
void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
|
||||
void _render_shadow_process();
|
||||
void _render_shadow_end();
|
||||
|
||||
/* Render Scene */
|
||||
void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections);
|
||||
void _process_ssil(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections, const Transform3D &p_transform);
|
||||
void _copy_framebuffer_to_ssil(Ref<RenderSceneBuffersRD> p_render_buffers);
|
||||
void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer);
|
||||
void _process_ssr(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_dest_framebuffer, const RID *p_normal_buffer_slices, RID p_specular_buffer, const RID *p_metallic_slices, RID p_environment, const Projection *p_projections, const Vector3 *p_eye_offsets, bool p_use_additive);
|
||||
void _process_sss(Ref<RenderSceneBuffersRD> p_render_buffers, const Projection &p_camera);
|
||||
|
||||
/* Debug */
|
||||
void _debug_draw_cluster(Ref<RenderSceneBuffersRD> p_render_buffers);
|
||||
|
||||
protected:
|
||||
/* setup */
|
||||
|
||||
virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
|
||||
virtual void environment_set_ssao_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) override;
|
||||
virtual void environment_set_ssil_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) override;
|
||||
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override;
|
||||
|
||||
virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override;
|
||||
virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override;
|
||||
|
||||
/* Rendering */
|
||||
|
||||
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override;
|
||||
virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data) override;
|
||||
|
||||
virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override;
|
||||
virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
|
||||
virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override;
|
||||
virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override;
|
||||
|
||||
public:
|
||||
static RenderForwardClustered *get_singleton() { return singleton; }
|
||||
|
||||
ClusterBuilderSharedDataRD *get_cluster_builder_shared() { return &cluster_builder_shared; }
|
||||
RendererRD::SSEffects *get_ss_effects() { return ss_effects; }
|
||||
|
||||
/* callback from updating our lighting UBOs, used to populate cluster builder */
|
||||
virtual void setup_added_reflection_probe(const Transform3D &p_transform, const Vector3 &p_half_size) override;
|
||||
virtual void setup_added_light(const RS::LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture) override;
|
||||
virtual void setup_added_decal(const Transform3D &p_transform, const Vector3 &p_half_size) override;
|
||||
|
||||
virtual void base_uniforms_changed() override;
|
||||
|
||||
/* SDFGI UPDATE */
|
||||
|
||||
virtual void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override;
|
||||
virtual int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override;
|
||||
virtual AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override;
|
||||
virtual uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override;
|
||||
RID sdfgi_get_ubo() const { return gi.sdfgi_ubo; }
|
||||
|
||||
/* GEOMETRY INSTANCE */
|
||||
|
||||
virtual RenderGeometryInstance *geometry_instance_create(RID p_base) override;
|
||||
virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override;
|
||||
|
||||
virtual uint32_t geometry_instance_get_pair_mask() override;
|
||||
|
||||
virtual bool free(RID p_rid) override;
|
||||
|
||||
RenderForwardClustered();
|
||||
~RenderForwardClustered();
|
||||
};
|
||||
} // namespace RendererSceneRenderImplementation
|
||||
|
||||
#endif // RENDER_FORWARD_CLUSTERED_H
|
||||
|
|
@ -0,0 +1,871 @@
|
|||
/**************************************************************************/
|
||||
/* scene_shader_forward_clustered.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 "scene_shader_forward_clustered.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/math_defs.h"
|
||||
#include "render_forward_clustered.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
|
||||
using namespace RendererSceneRenderImplementation;
|
||||
|
||||
void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
code = p_code;
|
||||
valid = false;
|
||||
ubo_size = 0;
|
||||
uniforms.clear();
|
||||
|
||||
if (code.is_empty()) {
|
||||
return; //just invalid, but no error
|
||||
}
|
||||
|
||||
ShaderCompiler::GeneratedCode gen_code;
|
||||
|
||||
int blend_mode = BLEND_MODE_MIX;
|
||||
int depth_testi = DEPTH_TEST_ENABLED;
|
||||
int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
|
||||
int cull_modei = CULL_BACK;
|
||||
|
||||
uses_point_size = false;
|
||||
uses_alpha = false;
|
||||
uses_alpha_clip = false;
|
||||
uses_alpha_antialiasing = false;
|
||||
uses_blend_alpha = false;
|
||||
uses_depth_prepass_alpha = false;
|
||||
uses_discard = false;
|
||||
uses_roughness = false;
|
||||
uses_normal = false;
|
||||
uses_tangent = false;
|
||||
bool uses_normal_map = false;
|
||||
bool wireframe = false;
|
||||
|
||||
unshaded = false;
|
||||
uses_vertex = false;
|
||||
uses_position = false;
|
||||
uses_sss = false;
|
||||
uses_transmittance = false;
|
||||
uses_time = false;
|
||||
writes_modelview_or_projection = false;
|
||||
uses_world_coordinates = false;
|
||||
uses_particle_trails = false;
|
||||
|
||||
int depth_drawi = DEPTH_DRAW_OPAQUE;
|
||||
|
||||
ShaderCompiler::IdentifierActions actions;
|
||||
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
|
||||
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
|
||||
actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT;
|
||||
|
||||
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD);
|
||||
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
|
||||
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
|
||||
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
|
||||
actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);
|
||||
|
||||
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
|
||||
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
|
||||
|
||||
actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED);
|
||||
actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE);
|
||||
actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS);
|
||||
|
||||
actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
|
||||
|
||||
actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
|
||||
actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
|
||||
actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
|
||||
|
||||
actions.render_mode_flags["unshaded"] = &unshaded;
|
||||
actions.render_mode_flags["wireframe"] = &wireframe;
|
||||
actions.render_mode_flags["particle_trails"] = &uses_particle_trails;
|
||||
actions.render_mode_flags["world_vertex_coords"] = &uses_world_coordinates;
|
||||
|
||||
actions.usage_flag_pointers["ALPHA"] = &uses_alpha;
|
||||
actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip;
|
||||
actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip;
|
||||
actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing;
|
||||
actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing;
|
||||
actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha;
|
||||
|
||||
actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss;
|
||||
actions.usage_flag_pointers["SSS_TRANSMITTANCE_DEPTH"] = &uses_transmittance;
|
||||
|
||||
actions.usage_flag_pointers["DISCARD"] = &uses_discard;
|
||||
actions.usage_flag_pointers["TIME"] = &uses_time;
|
||||
actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
|
||||
actions.usage_flag_pointers["NORMAL"] = &uses_normal;
|
||||
actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
|
||||
|
||||
actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
|
||||
actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
|
||||
|
||||
actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent;
|
||||
|
||||
actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection;
|
||||
actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection;
|
||||
actions.write_flag_pointers["VERTEX"] = &uses_vertex;
|
||||
actions.write_flag_pointers["POSITION"] = &uses_position;
|
||||
|
||||
actions.uniforms = &uniforms;
|
||||
|
||||
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
|
||||
Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
|
||||
|
||||
if (version.is_null()) {
|
||||
version = shader_singleton->shader.version_create();
|
||||
}
|
||||
|
||||
depth_draw = DepthDraw(depth_drawi);
|
||||
depth_test = DepthTest(depth_testi);
|
||||
cull_mode = Cull(cull_modei);
|
||||
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
|
||||
uses_screen_texture = gen_code.uses_screen_texture;
|
||||
uses_depth_texture = gen_code.uses_depth_texture;
|
||||
uses_normal_texture = gen_code.uses_normal_roughness_texture;
|
||||
uses_vertex_time = gen_code.uses_vertex_time;
|
||||
uses_fragment_time = gen_code.uses_fragment_time;
|
||||
uses_normal |= uses_normal_map;
|
||||
uses_tangent |= uses_normal_map;
|
||||
|
||||
#if 0
|
||||
print_line("**compiling shader:");
|
||||
print_line("**defines:\n");
|
||||
for (int i = 0; i < gen_code.defines.size(); i++) {
|
||||
print_line(gen_code.defines[i]);
|
||||
}
|
||||
|
||||
HashMap<String, String>::Iterator el = gen_code.code.begin();
|
||||
while (el) {
|
||||
print_line("\n**code " + el->key + ":\n" + el->value);
|
||||
++el;
|
||||
}
|
||||
|
||||
print_line("\n**uniforms:\n" + gen_code.uniforms);
|
||||
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
|
||||
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
|
||||
#endif
|
||||
shader_singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
|
||||
ERR_FAIL_COND(!shader_singleton->shader.version_is_valid(version));
|
||||
|
||||
ubo_size = gen_code.uniform_total_size;
|
||||
ubo_offsets = gen_code.uniform_offsets;
|
||||
texture_uniforms = gen_code.texture_uniforms;
|
||||
|
||||
//blend modes
|
||||
|
||||
// if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
|
||||
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
|
||||
blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
|
||||
}
|
||||
|
||||
RD::PipelineColorBlendState::Attachment blend_attachment;
|
||||
|
||||
switch (blend_mode) {
|
||||
case BLEND_MODE_MIX: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_ADD: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_SUB: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_MUL: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
} break;
|
||||
case BLEND_MODE_ALPHA_TO_COVERAGE: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
} break;
|
||||
case BLEND_MODE_PREMULT_ALPHA: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
uses_blend_alpha = true; // Force alpha used because of blend.
|
||||
} break;
|
||||
}
|
||||
|
||||
// Color pass -> attachment 0: Color/Diffuse, attachment 1: Separate Specular, attachment 2: Motion Vectors
|
||||
RD::PipelineColorBlendState blend_state_color_blend;
|
||||
blend_state_color_blend.attachments = { blend_attachment, RD::PipelineColorBlendState::Attachment(), RD::PipelineColorBlendState::Attachment() };
|
||||
RD::PipelineColorBlendState blend_state_color_opaque = RD::PipelineColorBlendState::create_disabled(3);
|
||||
RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(1);
|
||||
RD::PipelineColorBlendState blend_state_depth_normal_roughness_giprobe = RD::PipelineColorBlendState::create_disabled(2);
|
||||
|
||||
//update pipelines
|
||||
|
||||
RD::PipelineDepthStencilState depth_stencil_state;
|
||||
|
||||
if (depth_test != DEPTH_TEST_DISABLED) {
|
||||
depth_stencil_state.enable_depth_test = true;
|
||||
depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
|
||||
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
|
||||
}
|
||||
bool depth_pre_pass_enabled = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
|
||||
|
||||
for (int i = 0; i < CULL_VARIANT_MAX; i++) {
|
||||
RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
|
||||
};
|
||||
|
||||
RD::PolygonCullMode cull_mode_rd = cull_mode_rd_table[i][cull_mode];
|
||||
|
||||
for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
|
||||
RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
|
||||
RD::RENDER_PRIMITIVE_POINTS,
|
||||
RD::RENDER_PRIMITIVE_LINES,
|
||||
RD::RENDER_PRIMITIVE_LINESTRIPS,
|
||||
RD::RENDER_PRIMITIVE_TRIANGLES,
|
||||
RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
|
||||
};
|
||||
|
||||
RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j];
|
||||
|
||||
for (int k = 0; k < PIPELINE_VERSION_MAX; k++) {
|
||||
ShaderVersion shader_version;
|
||||
static const ShaderVersion shader_version_table[PIPELINE_VERSION_MAX] = {
|
||||
SHADER_VERSION_DEPTH_PASS,
|
||||
SHADER_VERSION_DEPTH_PASS_DP,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_SDF,
|
||||
SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
|
||||
SHADER_VERSION_COLOR_PASS,
|
||||
};
|
||||
|
||||
shader_version = shader_version_table[k];
|
||||
|
||||
if (!static_cast<SceneShaderForwardClustered *>(singleton)->shader.is_variant_enabled(shader_version)) {
|
||||
continue;
|
||||
}
|
||||
RD::PipelineRasterizationState raster_state;
|
||||
raster_state.cull_mode = cull_mode_rd;
|
||||
raster_state.wireframe = wireframe;
|
||||
|
||||
if (k == PIPELINE_VERSION_COLOR_PASS) {
|
||||
for (int l = 0; l < PIPELINE_COLOR_PASS_FLAG_COUNT; l++) {
|
||||
if (!shader_singleton->valid_color_pass_pipelines[l]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
|
||||
|
||||
RD::PipelineColorBlendState blend_state;
|
||||
RD::PipelineMultisampleState multisample_state;
|
||||
|
||||
int shader_flags = 0;
|
||||
if (l & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) {
|
||||
if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
|
||||
multisample_state.enable_alpha_to_coverage = true;
|
||||
} else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
|
||||
multisample_state.enable_alpha_to_coverage = true;
|
||||
multisample_state.enable_alpha_to_one = true;
|
||||
}
|
||||
|
||||
blend_state = blend_state_color_blend;
|
||||
|
||||
if (depth_draw == DEPTH_DRAW_OPAQUE) {
|
||||
depth_stencil.enable_depth_write = false; //alpha does not draw depth
|
||||
}
|
||||
} else {
|
||||
blend_state = blend_state_color_opaque;
|
||||
|
||||
if (depth_pre_pass_enabled) {
|
||||
// We already have a depth from the depth pre-pass, there is no need to write it again.
|
||||
// In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL.
|
||||
// This way we can use the early depth test to discard transparent fragments before the fragment shader even starts.
|
||||
depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL;
|
||||
depth_stencil.enable_depth_write = false;
|
||||
}
|
||||
|
||||
if (l & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR) {
|
||||
shader_flags |= SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (l & PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS) {
|
||||
shader_flags |= SHADER_COLOR_PASS_FLAG_MOTION_VECTORS;
|
||||
}
|
||||
|
||||
if (l & PIPELINE_COLOR_PASS_FLAG_LIGHTMAP) {
|
||||
shader_flags |= SHADER_COLOR_PASS_FLAG_LIGHTMAP;
|
||||
}
|
||||
|
||||
if (l & PIPELINE_COLOR_PASS_FLAG_MULTIVIEW) {
|
||||
shader_flags |= SHADER_COLOR_PASS_FLAG_MULTIVIEW;
|
||||
}
|
||||
|
||||
int variant = shader_version + shader_flags;
|
||||
|
||||
if (!static_cast<SceneShaderForwardClustered *>(singleton)->shader.is_variant_enabled(variant)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RID shader_variant = shader_singleton->shader.version_get_shader(version, variant);
|
||||
color_pipelines[i][j][l].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
|
||||
}
|
||||
} else {
|
||||
RD::PipelineColorBlendState blend_state;
|
||||
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
|
||||
RD::PipelineMultisampleState multisample_state;
|
||||
|
||||
if (k == PIPELINE_VERSION_DEPTH_PASS || k == PIPELINE_VERSION_DEPTH_PASS_DP || k == PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW) {
|
||||
//none, leave empty
|
||||
} else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW) {
|
||||
blend_state = blend_state_depth_normal_roughness;
|
||||
} else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW) {
|
||||
blend_state = blend_state_depth_normal_roughness_giprobe;
|
||||
} else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL) {
|
||||
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
|
||||
} else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_SDF) {
|
||||
blend_state = RD::PipelineColorBlendState(); //no color targets for SDF
|
||||
}
|
||||
|
||||
RID shader_variant = shader_singleton->shader.version_get_shader(version, shader_version);
|
||||
pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
bool SceneShaderForwardClustered::ShaderData::is_animated() const {
|
||||
return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
|
||||
}
|
||||
|
||||
bool SceneShaderForwardClustered::ShaderData::casts_shadows() const {
|
||||
bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
|
||||
bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
|
||||
bool has_alpha = has_base_alpha || uses_blend_alpha;
|
||||
|
||||
return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
|
||||
}
|
||||
|
||||
RS::ShaderNativeSourceCode SceneShaderForwardClustered::ShaderData::get_native_source_code() const {
|
||||
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
|
||||
|
||||
return shader_singleton->shader.version_get_native_source_code(version);
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered::ShaderData::ShaderData() :
|
||||
shader_list_element(this) {
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered::ShaderData::~ShaderData() {
|
||||
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
|
||||
ERR_FAIL_NULL(shader_singleton);
|
||||
//pipeline variants will clear themselves if shader is gone
|
||||
if (version.is_valid()) {
|
||||
shader_singleton->shader.version_free(version);
|
||||
}
|
||||
}
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *SceneShaderForwardClustered::_create_shader_func() {
|
||||
ShaderData *shader_data = memnew(ShaderData);
|
||||
singleton->shader_list.add(&shader_data->shader_list_element);
|
||||
return shader_data;
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::MaterialData::set_render_priority(int p_priority) {
|
||||
priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) {
|
||||
next_pass = p_pass;
|
||||
}
|
||||
|
||||
bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
|
||||
SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
|
||||
|
||||
return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true);
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered::MaterialData::~MaterialData() {
|
||||
free_parameters_uniform_set(uniform_set);
|
||||
}
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *SceneShaderForwardClustered::_create_material_func(ShaderData *p_shader) {
|
||||
MaterialData *material_data = memnew(MaterialData);
|
||||
material_data->shader_data = p_shader;
|
||||
//update will happen later anyway so do nothing.
|
||||
return material_data;
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered *SceneShaderForwardClustered::singleton = nullptr;
|
||||
|
||||
SceneShaderForwardClustered::SceneShaderForwardClustered() {
|
||||
// there should be only one of these, contained within our RenderFM singleton.
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered::~SceneShaderForwardClustered() {
|
||||
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
|
||||
|
||||
RD::get_singleton()->free(default_vec4_xform_buffer);
|
||||
RD::get_singleton()->free(shadow_sampler);
|
||||
|
||||
material_storage->shader_free(overdraw_material_shader);
|
||||
material_storage->shader_free(default_shader);
|
||||
material_storage->shader_free(debug_shadow_splits_material_shader);
|
||||
|
||||
material_storage->material_free(overdraw_material);
|
||||
material_storage->material_free(default_material);
|
||||
material_storage->material_free(debug_shadow_splits_material);
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::init(const String p_defines) {
|
||||
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
|
||||
|
||||
{
|
||||
Vector<ShaderRD::VariantDefine> shader_versions;
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
|
||||
|
||||
Vector<String> color_pass_flags = {
|
||||
"\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR
|
||||
"\n#define USE_LIGHTMAP\n", // SHADER_COLOR_PASS_FLAG_LIGHTMAP
|
||||
"\n#define USE_MULTIVIEW\n", // SHADER_COLOR_PASS_FLAG_MULTIVIEW
|
||||
"\n#define MOTION_VECTORS\n", // SHADER_COLOR_PASS_FLAG_MOTION_VECTORS
|
||||
};
|
||||
|
||||
for (int i = 0; i < SHADER_COLOR_PASS_FLAG_COUNT; i++) {
|
||||
String version = "";
|
||||
for (int j = 0; (1 << j) < SHADER_COLOR_PASS_FLAG_COUNT; j += 1) {
|
||||
if ((1 << j) & i) {
|
||||
version += color_pass_flags[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Assign a group based on what features this pass contains.
|
||||
ShaderGroup group = SHADER_GROUP_BASE;
|
||||
bool advanced_group = (i & SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR) || (i & SHADER_COLOR_PASS_FLAG_LIGHTMAP) || (i & SHADER_COLOR_PASS_FLAG_MOTION_VECTORS);
|
||||
bool multiview_group = i & SHADER_COLOR_PASS_FLAG_MULTIVIEW;
|
||||
if (advanced_group && multiview_group) {
|
||||
group = SHADER_GROUP_ADVANCED_MULTIVIEW;
|
||||
} else if (advanced_group) {
|
||||
group = SHADER_GROUP_ADVANCED;
|
||||
} else if (multiview_group) {
|
||||
group = SHADER_GROUP_MULTIVIEW;
|
||||
}
|
||||
|
||||
shader_versions.push_back(ShaderRD::VariantDefine(group, version, false));
|
||||
}
|
||||
|
||||
shader.initialize(shader_versions, p_defines);
|
||||
|
||||
if (RendererCompositorRD::get_singleton()->is_xr_enabled()) {
|
||||
shader.enable_group(SHADER_GROUP_MULTIVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
// Set flag to true if a combination is valid.
|
||||
// The only invalid combinations are those that include both TRANSPARENT and SEPARATE_SPECULAR.
|
||||
for (int i = 0; i < PIPELINE_COLOR_PASS_FLAG_COUNT; i++) {
|
||||
if ((i & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) && (i & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR)) {
|
||||
valid_color_pass_pipelines[i] = false;
|
||||
} else {
|
||||
valid_color_pass_pipelines[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs);
|
||||
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs);
|
||||
|
||||
{
|
||||
//shader compiler
|
||||
ShaderCompiler::DefaultIdentifierActions actions;
|
||||
|
||||
actions.renames["MODEL_MATRIX"] = "read_model_matrix";
|
||||
actions.renames["MODEL_NORMAL_MATRIX"] = "model_normal_matrix";
|
||||
actions.renames["VIEW_MATRIX"] = "read_view_matrix";
|
||||
actions.renames["INV_VIEW_MATRIX"] = "inv_view_matrix";
|
||||
actions.renames["PROJECTION_MATRIX"] = "projection_matrix";
|
||||
actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix";
|
||||
actions.renames["MODELVIEW_MATRIX"] = "modelview";
|
||||
actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal";
|
||||
actions.renames["MAIN_CAM_INV_VIEW_MATRIX"] = "scene_data.main_cam_inv_view_matrix";
|
||||
|
||||
actions.renames["VERTEX"] = "vertex";
|
||||
actions.renames["NORMAL"] = "normal";
|
||||
actions.renames["TANGENT"] = "tangent";
|
||||
actions.renames["BINORMAL"] = "binormal";
|
||||
actions.renames["POSITION"] = "position";
|
||||
actions.renames["UV"] = "uv_interp";
|
||||
actions.renames["UV2"] = "uv2_interp";
|
||||
actions.renames["COLOR"] = "color_interp";
|
||||
actions.renames["POINT_SIZE"] = "gl_PointSize";
|
||||
actions.renames["INSTANCE_ID"] = "gl_InstanceIndex";
|
||||
actions.renames["VERTEX_ID"] = "gl_VertexIndex";
|
||||
|
||||
actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold";
|
||||
actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale";
|
||||
actions.renames["ALPHA_ANTIALIASING_EDGE"] = "alpha_antialiasing_edge";
|
||||
actions.renames["ALPHA_TEXTURE_COORDINATE"] = "alpha_texture_coordinate";
|
||||
|
||||
//builtins
|
||||
|
||||
actions.renames["TIME"] = "global_time";
|
||||
actions.renames["EXPOSURE"] = "(1.0 / scene_data_block.data.emissive_exposure_normalization)";
|
||||
actions.renames["PI"] = _MKSTR(Math_PI);
|
||||
actions.renames["TAU"] = _MKSTR(Math_TAU);
|
||||
actions.renames["E"] = _MKSTR(Math_E);
|
||||
actions.renames["VIEWPORT_SIZE"] = "read_viewport_size";
|
||||
|
||||
actions.renames["FRAGCOORD"] = "gl_FragCoord";
|
||||
actions.renames["FRONT_FACING"] = "gl_FrontFacing";
|
||||
actions.renames["NORMAL_MAP"] = "normal_map";
|
||||
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
|
||||
actions.renames["ALBEDO"] = "albedo";
|
||||
actions.renames["ALPHA"] = "alpha";
|
||||
actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
|
||||
actions.renames["METALLIC"] = "metallic";
|
||||
actions.renames["SPECULAR"] = "specular";
|
||||
actions.renames["ROUGHNESS"] = "roughness";
|
||||
actions.renames["RIM"] = "rim";
|
||||
actions.renames["RIM_TINT"] = "rim_tint";
|
||||
actions.renames["CLEARCOAT"] = "clearcoat";
|
||||
actions.renames["CLEARCOAT_ROUGHNESS"] = "clearcoat_roughness";
|
||||
actions.renames["ANISOTROPY"] = "anisotropy";
|
||||
actions.renames["ANISOTROPY_FLOW"] = "anisotropy_flow";
|
||||
actions.renames["SSS_STRENGTH"] = "sss_strength";
|
||||
actions.renames["SSS_TRANSMITTANCE_COLOR"] = "transmittance_color";
|
||||
actions.renames["SSS_TRANSMITTANCE_DEPTH"] = "transmittance_depth";
|
||||
actions.renames["SSS_TRANSMITTANCE_BOOST"] = "transmittance_boost";
|
||||
actions.renames["BACKLIGHT"] = "backlight";
|
||||
actions.renames["AO"] = "ao";
|
||||
actions.renames["AO_LIGHT_AFFECT"] = "ao_light_affect";
|
||||
actions.renames["EMISSION"] = "emission";
|
||||
actions.renames["POINT_COORD"] = "gl_PointCoord";
|
||||
actions.renames["INSTANCE_CUSTOM"] = "instance_custom";
|
||||
actions.renames["SCREEN_UV"] = "screen_uv";
|
||||
actions.renames["DEPTH"] = "gl_FragDepth";
|
||||
actions.renames["FOG"] = "fog";
|
||||
actions.renames["RADIANCE"] = "custom_radiance";
|
||||
actions.renames["IRRADIANCE"] = "custom_irradiance";
|
||||
actions.renames["BONE_INDICES"] = "bone_attrib";
|
||||
actions.renames["BONE_WEIGHTS"] = "weight_attrib";
|
||||
actions.renames["CUSTOM0"] = "custom0_attrib";
|
||||
actions.renames["CUSTOM1"] = "custom1_attrib";
|
||||
actions.renames["CUSTOM2"] = "custom2_attrib";
|
||||
actions.renames["CUSTOM3"] = "custom3_attrib";
|
||||
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
|
||||
actions.renames["LIGHT_VERTEX"] = "light_vertex";
|
||||
|
||||
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
|
||||
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
|
||||
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz";
|
||||
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
|
||||
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
|
||||
|
||||
actions.renames["VIEW_INDEX"] = "ViewIndex";
|
||||
actions.renames["VIEW_MONO_LEFT"] = "0";
|
||||
actions.renames["VIEW_RIGHT"] = "1";
|
||||
actions.renames["EYE_OFFSET"] = "eye_offset";
|
||||
|
||||
//for light
|
||||
actions.renames["VIEW"] = "view";
|
||||
actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
|
||||
actions.renames["LIGHT_COLOR"] = "light_color";
|
||||
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
|
||||
actions.renames["LIGHT"] = "light";
|
||||
actions.renames["ATTENUATION"] = "attenuation";
|
||||
actions.renames["DIFFUSE_LIGHT"] = "diffuse_light";
|
||||
actions.renames["SPECULAR_LIGHT"] = "specular_light";
|
||||
|
||||
actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n";
|
||||
actions.usage_defines["TANGENT"] = "#define TANGENT_USED\n";
|
||||
actions.usage_defines["BINORMAL"] = "@TANGENT";
|
||||
actions.usage_defines["RIM"] = "#define LIGHT_RIM_USED\n";
|
||||
actions.usage_defines["RIM_TINT"] = "@RIM";
|
||||
actions.usage_defines["CLEARCOAT"] = "#define LIGHT_CLEARCOAT_USED\n";
|
||||
actions.usage_defines["CLEARCOAT_ROUGHNESS"] = "@CLEARCOAT";
|
||||
actions.usage_defines["ANISOTROPY"] = "#define LIGHT_ANISOTROPY_USED\n";
|
||||
actions.usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY";
|
||||
actions.usage_defines["AO"] = "#define AO_USED\n";
|
||||
actions.usage_defines["AO_LIGHT_AFFECT"] = "#define AO_USED\n";
|
||||
actions.usage_defines["UV"] = "#define UV_USED\n";
|
||||
actions.usage_defines["UV2"] = "#define UV2_USED\n";
|
||||
actions.usage_defines["BONE_INDICES"] = "#define BONES_USED\n";
|
||||
actions.usage_defines["BONE_WEIGHTS"] = "#define WEIGHTS_USED\n";
|
||||
actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n";
|
||||
actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n";
|
||||
actions.usage_defines["CUSTOM2"] = "#define CUSTOM2_USED\n";
|
||||
actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
|
||||
actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
|
||||
actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
|
||||
actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
|
||||
actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
|
||||
actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
|
||||
actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n";
|
||||
actions.usage_defines["PREMUL_ALPHA_FACTOR"] = "#define PREMUL_ALPHA_USED\n";
|
||||
|
||||
actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n";
|
||||
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
|
||||
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
|
||||
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
|
||||
|
||||
actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
|
||||
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
|
||||
actions.usage_defines["BACKLIGHT"] = "#define LIGHT_BACKLIGHT_USED\n";
|
||||
actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n";
|
||||
|
||||
actions.usage_defines["FOG"] = "#define CUSTOM_FOG_USED\n";
|
||||
actions.usage_defines["RADIANCE"] = "#define CUSTOM_RADIANCE_USED\n";
|
||||
actions.usage_defines["IRRADIANCE"] = "#define CUSTOM_IRRADIANCE_USED\n";
|
||||
|
||||
actions.usage_defines["MODEL_MATRIX"] = "#define MODEL_MATRIX_USED\n";
|
||||
|
||||
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
|
||||
actions.render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
|
||||
actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n";
|
||||
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
|
||||
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
|
||||
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
|
||||
actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
|
||||
|
||||
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
|
||||
|
||||
if (!force_lambert) {
|
||||
actions.render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n";
|
||||
}
|
||||
|
||||
actions.render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n";
|
||||
actions.render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n";
|
||||
|
||||
actions.render_mode_defines["sss_mode_skin"] = "#define SSS_MODE_SKIN\n";
|
||||
|
||||
actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
|
||||
|
||||
actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
|
||||
actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
|
||||
actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
|
||||
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
|
||||
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
|
||||
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
|
||||
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
|
||||
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
|
||||
|
||||
actions.base_texture_binding_index = 1;
|
||||
actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
|
||||
actions.base_uniform_string = "material.";
|
||||
actions.base_varying_index = 12;
|
||||
|
||||
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
|
||||
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
|
||||
actions.global_buffer_array_variable = "global_shader_uniforms.data";
|
||||
actions.instance_uniform_index_variable = "instances.data[instance_index_interp].instance_uniforms_ofs";
|
||||
|
||||
actions.check_multiview_samplers = RendererCompositorRD::get_singleton()->is_xr_enabled(); // Make sure we check sampling multiview textures.
|
||||
|
||||
compiler.initialize(actions);
|
||||
}
|
||||
|
||||
{
|
||||
//default material and shader
|
||||
default_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(default_shader);
|
||||
material_storage->shader_set_code(default_shader, R"(
|
||||
// Default 3D material shader (Forward+).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
void vertex() {
|
||||
ROUGHNESS = 0.8;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(0.6);
|
||||
ROUGHNESS = 0.8;
|
||||
METALLIC = 0.2;
|
||||
}
|
||||
)");
|
||||
default_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(default_material);
|
||||
material_storage->material_set_shader(default_material, default_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
default_shader_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS);
|
||||
default_shader_sdfgi_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF);
|
||||
|
||||
default_material_shader_ptr = md->shader_data;
|
||||
default_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
overdraw_material_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(overdraw_material_shader);
|
||||
// Use relatively low opacity so that more "layers" of overlapping objects can be distinguished.
|
||||
material_storage->shader_set_code(overdraw_material_shader, R"(
|
||||
// 3D editor Overdraw debug draw mode shader (Forward+).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
render_mode blend_add, unshaded, fog_disabled;
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(0.4, 0.8, 0.8);
|
||||
ALPHA = 0.1;
|
||||
}
|
||||
)");
|
||||
overdraw_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(overdraw_material);
|
||||
material_storage->material_set_shader(overdraw_material, overdraw_material_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(overdraw_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
overdraw_material_shader_ptr = md->shader_data;
|
||||
overdraw_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
debug_shadow_splits_material_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(debug_shadow_splits_material_shader);
|
||||
material_storage->shader_set_code(debug_shadow_splits_material_shader, R"(
|
||||
// 3D debug shadow splits mode shader (Forward+).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
render_mode debug_shadow_splits, fog_disabled;
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(1.0, 1.0, 1.0);
|
||||
}
|
||||
)");
|
||||
debug_shadow_splits_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(debug_shadow_splits_material);
|
||||
material_storage->material_set_shader(debug_shadow_splits_material, debug_shadow_splits_material_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(debug_shadow_splits_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
debug_shadow_splits_material_shader_ptr = md->shader_data;
|
||||
debug_shadow_splits_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
default_vec4_xform_buffer = RD::get_singleton()->storage_buffer_create(256);
|
||||
Vector<RD::Uniform> uniforms;
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.append_id(default_vec4_xform_buffer);
|
||||
u.binding = 0;
|
||||
uniforms.push_back(u);
|
||||
|
||||
default_vec4_xform_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RenderForwardClustered::TRANSFORMS_UNIFORM_SET);
|
||||
}
|
||||
{
|
||||
RD::SamplerState sampler;
|
||||
sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler.enable_compare = true;
|
||||
sampler.compare_op = RD::COMPARE_OP_GREATER;
|
||||
shadow_sampler = RD::get_singleton()->sampler_create(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants) {
|
||||
default_specialization_constants = p_constants;
|
||||
for (SelfList<ShaderData> *E = shader_list.first(); E; E = E->next()) {
|
||||
for (int i = 0; i < ShaderData::CULL_VARIANT_MAX; i++) {
|
||||
for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
|
||||
for (int k = 0; k < SHADER_VERSION_MAX; k++) {
|
||||
E->self()->pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
|
||||
}
|
||||
for (int k = 0; k < PIPELINE_COLOR_PASS_FLAG_COUNT; k++) {
|
||||
E->self()->color_pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_multiview) {
|
||||
if (p_needs_multiview || RendererCompositorRD::get_singleton()->is_xr_enabled()) {
|
||||
shader.enable_group(SHADER_GROUP_ADVANCED_MULTIVIEW);
|
||||
}
|
||||
shader.enable_group(SHADER_GROUP_ADVANCED);
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
/**************************************************************************/
|
||||
/* scene_shader_forward_clustered.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SCENE_SHADER_FORWARD_CLUSTERED_H
|
||||
#define SCENE_SHADER_FORWARD_CLUSTERED_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl.gen.h"
|
||||
|
||||
namespace RendererSceneRenderImplementation {
|
||||
|
||||
class SceneShaderForwardClustered {
|
||||
private:
|
||||
static SceneShaderForwardClustered *singleton;
|
||||
|
||||
public:
|
||||
enum ShaderGroup {
|
||||
SHADER_GROUP_BASE, // Always compiled at the beginning.
|
||||
SHADER_GROUP_ADVANCED,
|
||||
SHADER_GROUP_MULTIVIEW,
|
||||
SHADER_GROUP_ADVANCED_MULTIVIEW,
|
||||
};
|
||||
|
||||
enum ShaderVersion {
|
||||
SHADER_VERSION_DEPTH_PASS,
|
||||
SHADER_VERSION_DEPTH_PASS_DP,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_SDF,
|
||||
SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
|
||||
SHADER_VERSION_COLOR_PASS,
|
||||
SHADER_VERSION_MAX
|
||||
};
|
||||
|
||||
enum ShaderColorPassFlags {
|
||||
SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 0,
|
||||
SHADER_COLOR_PASS_FLAG_LIGHTMAP = 1 << 1,
|
||||
SHADER_COLOR_PASS_FLAG_MULTIVIEW = 1 << 2,
|
||||
SHADER_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 3,
|
||||
SHADER_COLOR_PASS_FLAG_COUNT = 1 << 4
|
||||
};
|
||||
|
||||
enum PipelineVersion {
|
||||
PIPELINE_VERSION_DEPTH_PASS,
|
||||
PIPELINE_VERSION_DEPTH_PASS_DP,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_SDF,
|
||||
PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
|
||||
PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
|
||||
PIPELINE_VERSION_COLOR_PASS,
|
||||
PIPELINE_VERSION_MAX
|
||||
};
|
||||
|
||||
enum PipelineColorPassFlags {
|
||||
PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0, // Can't combine with SEPARATE_SPECULAR.
|
||||
PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1, // Can't combine with TRANSPARENT.
|
||||
PIPELINE_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2,
|
||||
PIPELINE_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3,
|
||||
PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4,
|
||||
PIPELINE_COLOR_PASS_FLAG_COUNT = 1 << 5,
|
||||
};
|
||||
|
||||
enum ShaderSpecializations {
|
||||
SHADER_SPECIALIZATION_FORWARD_GI = 1 << 0,
|
||||
SHADER_SPECIALIZATION_PROJECTOR = 1 << 1,
|
||||
SHADER_SPECIALIZATION_SOFT_SHADOWS = 1 << 2,
|
||||
SHADER_SPECIALIZATION_DIRECTIONAL_SOFT_SHADOWS = 1 << 3,
|
||||
};
|
||||
|
||||
struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
|
||||
enum BlendMode { //used internally
|
||||
BLEND_MODE_MIX,
|
||||
BLEND_MODE_ADD,
|
||||
BLEND_MODE_SUB,
|
||||
BLEND_MODE_MUL,
|
||||
BLEND_MODE_ALPHA_TO_COVERAGE,
|
||||
BLEND_MODE_PREMULT_ALPHA,
|
||||
};
|
||||
|
||||
enum DepthDraw {
|
||||
DEPTH_DRAW_DISABLED,
|
||||
DEPTH_DRAW_OPAQUE,
|
||||
DEPTH_DRAW_ALWAYS
|
||||
};
|
||||
|
||||
enum DepthTest {
|
||||
DEPTH_TEST_DISABLED,
|
||||
DEPTH_TEST_ENABLED
|
||||
};
|
||||
|
||||
enum Cull {
|
||||
CULL_DISABLED,
|
||||
CULL_FRONT,
|
||||
CULL_BACK
|
||||
};
|
||||
|
||||
enum CullVariant {
|
||||
CULL_VARIANT_NORMAL,
|
||||
CULL_VARIANT_REVERSED,
|
||||
CULL_VARIANT_DOUBLE_SIDED,
|
||||
CULL_VARIANT_MAX
|
||||
|
||||
};
|
||||
|
||||
enum AlphaAntiAliasing {
|
||||
ALPHA_ANTIALIASING_OFF,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
|
||||
};
|
||||
|
||||
bool valid = false;
|
||||
RID version;
|
||||
uint64_t vertex_input_mask = 0;
|
||||
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_VERSION_MAX];
|
||||
PipelineCacheRD color_pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_COLOR_PASS_FLAG_COUNT];
|
||||
|
||||
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size = 0;
|
||||
|
||||
String code;
|
||||
|
||||
DepthDraw depth_draw = DEPTH_DRAW_OPAQUE;
|
||||
DepthTest depth_test = DEPTH_TEST_ENABLED;
|
||||
|
||||
bool uses_point_size = false;
|
||||
bool uses_alpha = false;
|
||||
bool uses_blend_alpha = false;
|
||||
bool uses_alpha_clip = false;
|
||||
bool uses_alpha_antialiasing = false;
|
||||
bool uses_depth_prepass_alpha = false;
|
||||
bool uses_discard = false;
|
||||
bool uses_roughness = false;
|
||||
bool uses_normal = false;
|
||||
bool uses_tangent = false;
|
||||
bool uses_particle_trails = false;
|
||||
|
||||
bool unshaded = false;
|
||||
bool uses_vertex = false;
|
||||
bool uses_position = false;
|
||||
bool uses_sss = false;
|
||||
bool uses_transmittance = false;
|
||||
bool uses_screen_texture = false;
|
||||
bool uses_depth_texture = false;
|
||||
bool uses_normal_texture = false;
|
||||
bool uses_time = false;
|
||||
bool uses_vertex_time = false;
|
||||
bool uses_fragment_time = false;
|
||||
bool writes_modelview_or_projection = false;
|
||||
bool uses_world_coordinates = false;
|
||||
bool uses_screen_texture_mipmaps = false;
|
||||
Cull cull_mode = CULL_DISABLED;
|
||||
|
||||
uint64_t last_pass = 0;
|
||||
uint32_t index = 0;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
|
||||
|
||||
SelfList<ShaderData> shader_list_element;
|
||||
ShaderData();
|
||||
virtual ~ShaderData();
|
||||
};
|
||||
|
||||
SelfList<ShaderData>::List shader_list;
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *_create_shader_func();
|
||||
static RendererRD::MaterialStorage::ShaderData *_create_shader_funcs() {
|
||||
return static_cast<SceneShaderForwardClustered *>(singleton)->_create_shader_func();
|
||||
}
|
||||
|
||||
struct MaterialData : public RendererRD::MaterialStorage::MaterialData {
|
||||
ShaderData *shader_data = nullptr;
|
||||
RID uniform_set;
|
||||
uint64_t last_pass = 0;
|
||||
uint32_t index = 0;
|
||||
RID next_pass;
|
||||
uint8_t priority;
|
||||
virtual void set_render_priority(int p_priority);
|
||||
virtual void set_next_pass(RID p_pass);
|
||||
virtual bool update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~MaterialData();
|
||||
};
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *_create_material_func(ShaderData *p_shader);
|
||||
static RendererRD::MaterialStorage::MaterialData *_create_material_funcs(RendererRD::MaterialStorage::ShaderData *p_shader) {
|
||||
return static_cast<SceneShaderForwardClustered *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader));
|
||||
}
|
||||
|
||||
SceneForwardClusteredShaderRD shader;
|
||||
ShaderCompiler compiler;
|
||||
|
||||
RID default_shader;
|
||||
RID default_material;
|
||||
RID overdraw_material_shader;
|
||||
RID overdraw_material;
|
||||
RID debug_shadow_splits_material_shader;
|
||||
RID debug_shadow_splits_material;
|
||||
RID default_shader_rd;
|
||||
RID default_shader_sdfgi_rd;
|
||||
|
||||
RID default_vec4_xform_buffer;
|
||||
RID default_vec4_xform_uniform_set;
|
||||
|
||||
RID shadow_sampler;
|
||||
|
||||
RID default_material_uniform_set;
|
||||
ShaderData *default_material_shader_ptr = nullptr;
|
||||
|
||||
RID overdraw_material_uniform_set;
|
||||
ShaderData *overdraw_material_shader_ptr = nullptr;
|
||||
|
||||
RID debug_shadow_splits_material_uniform_set;
|
||||
ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
|
||||
|
||||
Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
|
||||
bool valid_color_pass_pipelines[PIPELINE_COLOR_PASS_FLAG_COUNT];
|
||||
SceneShaderForwardClustered();
|
||||
~SceneShaderForwardClustered();
|
||||
|
||||
void init(const String p_defines);
|
||||
void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
|
||||
void enable_advanced_shader_group(bool p_needs_multiview = false);
|
||||
};
|
||||
|
||||
} // namespace RendererSceneRenderImplementation
|
||||
|
||||
#endif // SCENE_SHADER_FORWARD_CLUSTERED_H
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.servers_sources, "*.cpp")
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,591 @@
|
|||
/**************************************************************************/
|
||||
/* render_forward_mobile.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDER_FORWARD_MOBILE_H
|
||||
#define RENDER_FORWARD_MOBILE_H
|
||||
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h"
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/utilities.h"
|
||||
|
||||
#define RB_SCOPE_MOBILE SNAME("mobile")
|
||||
|
||||
namespace RendererSceneRenderImplementation {
|
||||
|
||||
class RenderForwardMobile : public RendererSceneRenderRD {
|
||||
friend SceneShaderForwardMobile;
|
||||
|
||||
protected:
|
||||
struct GeometryInstanceSurfaceDataCache;
|
||||
|
||||
private:
|
||||
static RenderForwardMobile *singleton;
|
||||
|
||||
/* Scene Shader */
|
||||
|
||||
enum {
|
||||
SCENE_UNIFORM_SET = 0,
|
||||
RENDER_PASS_UNIFORM_SET = 1,
|
||||
TRANSFORMS_UNIFORM_SET = 2,
|
||||
MATERIAL_UNIFORM_SET = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
SPEC_CONSTANT_USING_PROJECTOR = 0,
|
||||
SPEC_CONSTANT_USING_SOFT_SHADOWS = 1,
|
||||
SPEC_CONSTANT_USING_DIRECTIONAL_SOFT_SHADOWS = 2,
|
||||
|
||||
SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 3,
|
||||
SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 4,
|
||||
SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 5,
|
||||
SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 6,
|
||||
|
||||
SPEC_CONSTANT_DECAL_USE_MIPMAPS = 7,
|
||||
SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS = 8,
|
||||
|
||||
SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 9,
|
||||
SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 10,
|
||||
SPEC_CONSTANT_DISABLE_REFLECTION_PROBES = 11,
|
||||
SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 12,
|
||||
|
||||
SPEC_CONSTANT_DISABLE_DECALS = 13,
|
||||
SPEC_CONSTANT_DISABLE_FOG = 14,
|
||||
SPEC_CONSTANT_USE_DEPTH_FOG = 16,
|
||||
SPEC_CONSTANT_IS_MULTIMESH = 17,
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_LIGHTMAPS = 8,
|
||||
MAX_RDL_CULL = 8, // maximum number of reflection probes, decals or lights we can cull per geometry instance
|
||||
INSTANCE_DATA_BUFFER_MIN_SIZE = 4096
|
||||
};
|
||||
|
||||
enum RenderListType {
|
||||
RENDER_LIST_OPAQUE, //used for opaque objects
|
||||
RENDER_LIST_ALPHA, //used for transparent objects
|
||||
RENDER_LIST_SECONDARY, //used for shadows and other objects
|
||||
RENDER_LIST_MAX
|
||||
};
|
||||
|
||||
/* Scene Shader */
|
||||
|
||||
SceneShaderForwardMobile scene_shader;
|
||||
|
||||
/* Render Buffer */
|
||||
|
||||
class RenderBufferDataForwardMobile : public RenderBufferCustomDataRD {
|
||||
GDCLASS(RenderBufferDataForwardMobile, RenderBufferCustomDataRD);
|
||||
|
||||
public:
|
||||
enum FramebufferConfigType {
|
||||
FB_CONFIG_RENDER_PASS, // Single pass framebuffer for normal rendering.
|
||||
FB_CONFIG_RENDER_AND_POST_PASS, // Two subpasses, one for normal rendering, one for post processing.
|
||||
FB_CONFIG_MAX
|
||||
};
|
||||
|
||||
RID get_color_fbs(FramebufferConfigType p_config_type);
|
||||
virtual void free_data() override;
|
||||
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
|
||||
|
||||
private:
|
||||
RenderSceneBuffersRD *render_buffers = nullptr;
|
||||
};
|
||||
|
||||
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
|
||||
/* Rendering */
|
||||
|
||||
enum PassMode {
|
||||
PASS_MODE_COLOR,
|
||||
// PASS_MODE_COLOR_SPECULAR,
|
||||
PASS_MODE_COLOR_TRANSPARENT,
|
||||
PASS_MODE_SHADOW,
|
||||
PASS_MODE_SHADOW_DP,
|
||||
// PASS_MODE_DEPTH,
|
||||
// PASS_MODE_DEPTH_NORMAL_ROUGHNESS,
|
||||
// PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI,
|
||||
PASS_MODE_DEPTH_MATERIAL,
|
||||
// PASS_MODE_SDF,
|
||||
};
|
||||
|
||||
struct RenderElementInfo;
|
||||
|
||||
struct RenderListParameters {
|
||||
GeometryInstanceSurfaceDataCache **elements = nullptr;
|
||||
RenderElementInfo *element_info = nullptr;
|
||||
int element_count = 0;
|
||||
bool reverse_cull = false;
|
||||
PassMode pass_mode = PASS_MODE_COLOR;
|
||||
// bool no_gi = false;
|
||||
uint32_t view_count = 1;
|
||||
RID render_pass_uniform_set;
|
||||
bool force_wireframe = false;
|
||||
Vector2 uv_offset;
|
||||
uint32_t spec_constant_base_flags = 0;
|
||||
float lod_distance_multiplier = 0.0;
|
||||
float screen_mesh_lod_threshold = 0.0;
|
||||
RD::FramebufferFormatID framebuffer_format = 0;
|
||||
uint32_t element_offset = 0;
|
||||
uint32_t subpass = 0;
|
||||
|
||||
RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) {
|
||||
elements = p_elements;
|
||||
element_info = p_element_info;
|
||||
element_count = p_element_count;
|
||||
reverse_cull = p_reverse_cull;
|
||||
pass_mode = p_pass_mode;
|
||||
// no_gi = p_no_gi;
|
||||
view_count = p_view_count;
|
||||
render_pass_uniform_set = p_render_pass_uniform_set;
|
||||
force_wireframe = p_force_wireframe;
|
||||
uv_offset = p_uv_offset;
|
||||
lod_distance_multiplier = p_lod_distance_multiplier;
|
||||
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
|
||||
element_offset = p_element_offset;
|
||||
spec_constant_base_flags = p_spec_constant_base_flags;
|
||||
}
|
||||
};
|
||||
|
||||
/* Render shadows */
|
||||
|
||||
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
|
||||
void _render_shadow_begin();
|
||||
void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Transform3D &p_main_cam_transform = Transform3D());
|
||||
void _render_shadow_process();
|
||||
void _render_shadow_end();
|
||||
|
||||
/* Render Scene */
|
||||
|
||||
RID _setup_render_pass_uniform_set(RenderListType p_render_list, const RenderDataRD *p_render_data, RID p_radiance_texture, const RendererRD::MaterialStorage::Samplers &p_samplers, bool p_use_directional_shadow_atlas = false, int p_index = 0);
|
||||
void _pre_opaque_render(RenderDataRD *p_render_data);
|
||||
|
||||
uint64_t lightmap_texture_array_version = 0xFFFFFFFF;
|
||||
|
||||
void _update_render_base_uniform_set();
|
||||
|
||||
void _update_instance_data_buffer(RenderListType p_render_list);
|
||||
void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
|
||||
void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_append = false);
|
||||
|
||||
void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0);
|
||||
void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform);
|
||||
|
||||
RID render_base_uniform_set;
|
||||
LocalVector<RID> render_pass_uniform_sets;
|
||||
|
||||
/* Light map */
|
||||
|
||||
struct LightmapData {
|
||||
float normal_xform[12];
|
||||
float pad[3];
|
||||
float exposure_normalization;
|
||||
};
|
||||
|
||||
struct LightmapCaptureData {
|
||||
float sh[9 * 4];
|
||||
};
|
||||
|
||||
/* Scene state */
|
||||
|
||||
struct SceneState {
|
||||
LocalVector<RID> uniform_buffers;
|
||||
|
||||
struct PushConstant {
|
||||
float uv_offset[2];
|
||||
uint32_t base_index;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
struct InstanceData {
|
||||
float transform[16];
|
||||
uint32_t flags;
|
||||
uint32_t instance_uniforms_ofs; // Base offset in global buffer for instance variables.
|
||||
uint32_t gi_offset; // GI information when using lightmapping (VCT or lightmap index).
|
||||
uint32_t layer_mask = 1;
|
||||
float lightmap_uv_scale[4]; // Doubles as uv_offset when needed.
|
||||
uint32_t reflection_probes[2]; // Packed reflection probes.
|
||||
uint32_t omni_lights[2]; // Packed omni lights.
|
||||
uint32_t spot_lights[2]; // Packed spot lights.
|
||||
uint32_t decals[2]; // Packed spot lights.
|
||||
float compressed_aabb_position[4];
|
||||
float compressed_aabb_size[4];
|
||||
float uv_scale[4];
|
||||
};
|
||||
|
||||
RID instance_buffer[RENDER_LIST_MAX];
|
||||
uint32_t instance_buffer_size[RENDER_LIST_MAX] = { 0, 0, 0 };
|
||||
LocalVector<InstanceData> instance_data[RENDER_LIST_MAX];
|
||||
|
||||
// !BAS! We need to change lightmaps, we're not going to do this with a buffer but pushing the used lightmap in
|
||||
LightmapData lightmaps[MAX_LIGHTMAPS];
|
||||
RID lightmap_ids[MAX_LIGHTMAPS];
|
||||
bool lightmap_has_sh[MAX_LIGHTMAPS];
|
||||
uint32_t lightmaps_used = 0;
|
||||
uint32_t max_lightmaps;
|
||||
RID lightmap_buffer;
|
||||
|
||||
LightmapCaptureData *lightmap_captures = nullptr;
|
||||
uint32_t max_lightmap_captures;
|
||||
RID lightmap_capture_buffer;
|
||||
|
||||
bool used_screen_texture = false;
|
||||
bool used_normal_texture = false;
|
||||
bool used_depth_texture = false;
|
||||
bool used_sss = false;
|
||||
|
||||
struct ShadowPass {
|
||||
uint32_t element_from;
|
||||
uint32_t element_count;
|
||||
bool flip_cull;
|
||||
PassMode pass_mode;
|
||||
|
||||
RID rp_uniform_set;
|
||||
float lod_distance_multiplier;
|
||||
float screen_mesh_lod_threshold;
|
||||
|
||||
RID framebuffer;
|
||||
RD::InitialAction initial_depth_action;
|
||||
Rect2i rect;
|
||||
};
|
||||
|
||||
LocalVector<ShadowPass> shadow_passes;
|
||||
} scene_state;
|
||||
|
||||
/* Render List */
|
||||
|
||||
// !BAS! Render list can probably be reused between clustered and mobile?
|
||||
struct RenderList {
|
||||
LocalVector<GeometryInstanceSurfaceDataCache *> elements;
|
||||
LocalVector<RenderElementInfo> element_info;
|
||||
|
||||
void clear() {
|
||||
elements.clear();
|
||||
element_info.clear();
|
||||
}
|
||||
|
||||
//should eventually be replaced by radix
|
||||
|
||||
struct SortByKey {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_key() {
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
void sort_by_key_range(uint32_t p_from, uint32_t p_size) {
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr() + p_from, p_size);
|
||||
}
|
||||
|
||||
struct SortByDepth {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->owner->depth < B->owner->depth);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_depth() { //used for shadows
|
||||
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByDepth> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
struct SortByReverseDepthAndPriority {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurfaceDataCache *A, const GeometryInstanceSurfaceDataCache *B) const {
|
||||
return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_reverse_depth_and_priority() { //used for alpha
|
||||
|
||||
SortArray<GeometryInstanceSurfaceDataCache *, SortByReverseDepthAndPriority> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_element(GeometryInstanceSurfaceDataCache *p_element) {
|
||||
elements.push_back(p_element);
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderElementInfo {
|
||||
uint32_t uses_lightmap : 1;
|
||||
uint32_t lod_index : 8;
|
||||
uint32_t reserved : 23;
|
||||
};
|
||||
|
||||
template <PassMode p_pass_mode>
|
||||
_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
|
||||
|
||||
RenderList render_list[RENDER_LIST_MAX];
|
||||
|
||||
protected:
|
||||
/* setup */
|
||||
virtual void _update_shader_quality_settings() override;
|
||||
|
||||
virtual float _render_buffers_get_luminance_multiplier() override;
|
||||
virtual RD::DataFormat _render_buffers_get_color_format() override;
|
||||
virtual bool _render_buffers_can_be_storage() override;
|
||||
|
||||
virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) override;
|
||||
|
||||
virtual void environment_set_ssao_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) override{};
|
||||
virtual void environment_set_ssil_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) override{};
|
||||
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override{};
|
||||
|
||||
virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override{};
|
||||
virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override{};
|
||||
|
||||
/* Geometry instance */
|
||||
|
||||
class GeometryInstanceForwardMobile;
|
||||
|
||||
// When changing any of these enums, remember to change the corresponding enums in the shader files as well.
|
||||
enum {
|
||||
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
|
||||
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
|
||||
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
|
||||
INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 6,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8,
|
||||
INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9,
|
||||
INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 10,
|
||||
INSTANCE_DATA_FLAG_PARTICLES = 1 << 11,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
|
||||
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_SHIFT = 16,
|
||||
INSTANCE_DATA_FLAGS_PARTICLE_TRAIL_MASK = 0xFF,
|
||||
};
|
||||
|
||||
struct GeometryInstanceLightmapSH {
|
||||
Color sh[9];
|
||||
};
|
||||
|
||||
// Cached data for drawing surfaces
|
||||
struct GeometryInstanceSurfaceDataCache {
|
||||
enum {
|
||||
FLAG_PASS_DEPTH = 1,
|
||||
FLAG_PASS_OPAQUE = 2,
|
||||
FLAG_PASS_ALPHA = 4,
|
||||
FLAG_PASS_SHADOW = 8,
|
||||
FLAG_USES_SHARED_SHADOW_MATERIAL = 128,
|
||||
FLAG_USES_SUBSURFACE_SCATTERING = 2048,
|
||||
FLAG_USES_SCREEN_TEXTURE = 4096,
|
||||
FLAG_USES_DEPTH_TEXTURE = 8192,
|
||||
FLAG_USES_NORMAL_TEXTURE = 16384,
|
||||
FLAG_USES_DOUBLE_SIDED_SHADOWS = 32768,
|
||||
FLAG_USES_PARTICLE_TRAILS = 65536,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
// !BAS! CHECK BITS!!!
|
||||
|
||||
uint64_t surface_index : 10;
|
||||
uint64_t geometry_id : 32;
|
||||
uint64_t material_id_low : 16;
|
||||
|
||||
uint64_t material_id_hi : 16;
|
||||
uint64_t shader_id : 32;
|
||||
uint64_t uses_lightmap : 4; // sort by lightmap id here, not whether its yes/no (is 4 bits enough?)
|
||||
uint64_t depth_layer : 4;
|
||||
uint64_t priority : 8;
|
||||
|
||||
// uint64_t lod_index : 8; // no need to sort on LOD
|
||||
// uint64_t uses_forward_gi : 1; // no GI here, remove
|
||||
};
|
||||
struct {
|
||||
uint64_t sort_key1;
|
||||
uint64_t sort_key2;
|
||||
};
|
||||
} sort;
|
||||
|
||||
RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
|
||||
uint32_t flags = 0;
|
||||
uint32_t surface_index = 0;
|
||||
uint32_t lod_index = 0;
|
||||
|
||||
void *surface = nullptr;
|
||||
RID material_uniform_set;
|
||||
SceneShaderForwardMobile::ShaderData *shader = nullptr;
|
||||
SceneShaderForwardMobile::MaterialData *material = nullptr;
|
||||
|
||||
void *surface_shadow = nullptr;
|
||||
RID material_uniform_set_shadow;
|
||||
SceneShaderForwardMobile::ShaderData *shader_shadow = nullptr;
|
||||
|
||||
GeometryInstanceSurfaceDataCache *next = nullptr;
|
||||
GeometryInstanceForwardMobile *owner = nullptr;
|
||||
};
|
||||
|
||||
class GeometryInstanceForwardMobile : public RenderGeometryInstanceBase {
|
||||
public:
|
||||
//used during rendering
|
||||
RID transforms_uniform_set;
|
||||
bool use_projector = false;
|
||||
bool use_soft_shadow = false;
|
||||
bool store_transform_cache = true; // If true we copy our transform into our per-draw buffer, if false we use our transforms UBO and clear our per-draw transform.
|
||||
uint32_t instance_count = 0;
|
||||
uint32_t trail_steps = 1;
|
||||
|
||||
// lightmap
|
||||
uint32_t gi_offset_cache = 0; // !BAS! Should rename this to lightmap_offset_cache, in forward clustered this was shared between gi and lightmap
|
||||
RID lightmap_instance;
|
||||
Rect2 lightmap_uv_scale;
|
||||
uint32_t lightmap_slice_index;
|
||||
GeometryInstanceLightmapSH *lightmap_sh = nullptr;
|
||||
|
||||
// culled light info
|
||||
uint32_t reflection_probe_count = 0;
|
||||
RendererRD::ForwardID reflection_probes[MAX_RDL_CULL];
|
||||
uint32_t omni_light_count = 0;
|
||||
RendererRD::ForwardID omni_lights[MAX_RDL_CULL];
|
||||
uint32_t spot_light_count = 0;
|
||||
RendererRD::ForwardID spot_lights[MAX_RDL_CULL];
|
||||
uint32_t decals_count = 0;
|
||||
RendererRD::ForwardID decals[MAX_RDL_CULL];
|
||||
|
||||
GeometryInstanceSurfaceDataCache *surface_caches = nullptr;
|
||||
|
||||
// do we use this?
|
||||
SelfList<GeometryInstanceForwardMobile> dirty_list_element;
|
||||
|
||||
GeometryInstanceForwardMobile() :
|
||||
dirty_list_element(this) {}
|
||||
|
||||
virtual void _mark_dirty() override;
|
||||
|
||||
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
|
||||
virtual void set_lightmap_capture(const Color *p_sh9) override;
|
||||
|
||||
virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
|
||||
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
|
||||
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override;
|
||||
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
|
||||
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override;
|
||||
};
|
||||
|
||||
/* Rendering */
|
||||
|
||||
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override;
|
||||
|
||||
virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override;
|
||||
virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
|
||||
virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override;
|
||||
virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override;
|
||||
|
||||
/* Forward ID */
|
||||
|
||||
class ForwardIDStorageMobile : public RendererRD::ForwardIDStorage {
|
||||
public:
|
||||
struct ForwardIDAllocator {
|
||||
LocalVector<bool> allocations;
|
||||
LocalVector<uint8_t> map;
|
||||
LocalVector<uint64_t> last_pass;
|
||||
};
|
||||
|
||||
ForwardIDAllocator forward_id_allocators[RendererRD::FORWARD_ID_MAX];
|
||||
|
||||
public:
|
||||
virtual RendererRD::ForwardID allocate_forward_id(RendererRD::ForwardIDType p_type) override;
|
||||
virtual void free_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id) override;
|
||||
virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index, uint64_t p_last_pass) override;
|
||||
virtual bool uses_forward_ids() const override { return true; }
|
||||
};
|
||||
|
||||
ForwardIDStorageMobile *forward_id_storage_mobile = nullptr;
|
||||
|
||||
void fill_push_constant_instance_indices(SceneState::InstanceData *p_instance_data, const GeometryInstanceForwardMobile *p_instance);
|
||||
|
||||
virtual RendererRD::ForwardIDStorage *create_forward_id_storage() override {
|
||||
forward_id_storage_mobile = memnew(ForwardIDStorageMobile);
|
||||
return forward_id_storage_mobile;
|
||||
}
|
||||
|
||||
public:
|
||||
static RenderForwardMobile *get_singleton() { return singleton; }
|
||||
|
||||
virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth) override;
|
||||
|
||||
/* SDFGI UPDATE */
|
||||
|
||||
virtual void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
|
||||
virtual int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override { return 0; }
|
||||
virtual AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return AABB(); }
|
||||
virtual uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return 0; }
|
||||
|
||||
/* GEOMETRY INSTANCE */
|
||||
|
||||
static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
|
||||
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
|
||||
|
||||
SelfList<GeometryInstanceForwardMobile>::List geometry_instance_dirty_list;
|
||||
|
||||
PagedAllocator<GeometryInstanceForwardMobile> geometry_instance_alloc;
|
||||
PagedAllocator<GeometryInstanceSurfaceDataCache> geometry_instance_surface_alloc;
|
||||
PagedAllocator<GeometryInstanceLightmapSH> geometry_instance_lightmap_sh;
|
||||
|
||||
void _geometry_instance_add_surface_with_material(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
|
||||
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, RID p_mat_src, RID p_mesh);
|
||||
void _geometry_instance_add_surface(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
|
||||
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
|
||||
void _update_dirty_geometry_instances();
|
||||
|
||||
virtual RenderGeometryInstance *geometry_instance_create(RID p_base) override;
|
||||
virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override;
|
||||
|
||||
virtual uint32_t geometry_instance_get_pair_mask() override;
|
||||
|
||||
virtual bool free(RID p_rid) override;
|
||||
|
||||
virtual void base_uniforms_changed() override;
|
||||
|
||||
virtual bool is_dynamic_gi_supported() const override;
|
||||
virtual bool is_volumetric_supported() const override;
|
||||
virtual uint32_t get_max_elements() const override;
|
||||
|
||||
RenderForwardMobile();
|
||||
~RenderForwardMobile();
|
||||
};
|
||||
} // namespace RendererSceneRenderImplementation
|
||||
|
||||
#endif // RENDER_FORWARD_MOBILE_H
|
||||
|
|
@ -0,0 +1,780 @@
|
|||
/**************************************************************************/
|
||||
/* scene_shader_forward_mobile.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 "scene_shader_forward_mobile.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/math_defs.h"
|
||||
#include "render_forward_mobile.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
|
||||
using namespace RendererSceneRenderImplementation;
|
||||
|
||||
/* ShaderData */
|
||||
|
||||
void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
code = p_code;
|
||||
valid = false;
|
||||
ubo_size = 0;
|
||||
uniforms.clear();
|
||||
|
||||
if (code.is_empty()) {
|
||||
return; //just invalid, but no error
|
||||
}
|
||||
|
||||
ShaderCompiler::GeneratedCode gen_code;
|
||||
|
||||
int blend_mode = BLEND_MODE_MIX;
|
||||
int depth_testi = DEPTH_TEST_ENABLED;
|
||||
int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
|
||||
int cull = CULL_BACK;
|
||||
|
||||
uses_point_size = false;
|
||||
uses_alpha = false;
|
||||
uses_alpha_clip = false;
|
||||
uses_alpha_antialiasing = false;
|
||||
uses_blend_alpha = false;
|
||||
uses_depth_prepass_alpha = false;
|
||||
uses_discard = false;
|
||||
uses_roughness = false;
|
||||
uses_normal = false;
|
||||
uses_tangent = false;
|
||||
bool uses_normal_map = false;
|
||||
bool wireframe = false;
|
||||
|
||||
unshaded = false;
|
||||
uses_vertex = false;
|
||||
uses_sss = false;
|
||||
uses_transmittance = false;
|
||||
uses_time = false;
|
||||
writes_modelview_or_projection = false;
|
||||
uses_world_coordinates = false;
|
||||
uses_particle_trails = false;
|
||||
|
||||
int depth_drawi = DEPTH_DRAW_OPAQUE;
|
||||
|
||||
ShaderCompiler::IdentifierActions actions;
|
||||
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
|
||||
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
|
||||
actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT;
|
||||
|
||||
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_mode, BLEND_MODE_ADD);
|
||||
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
|
||||
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
|
||||
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
|
||||
actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);
|
||||
|
||||
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
|
||||
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
|
||||
|
||||
actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED);
|
||||
actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE);
|
||||
actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS);
|
||||
|
||||
actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
|
||||
|
||||
actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull, CULL_DISABLED);
|
||||
actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull, CULL_FRONT);
|
||||
actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull, CULL_BACK);
|
||||
|
||||
actions.render_mode_flags["unshaded"] = &unshaded;
|
||||
actions.render_mode_flags["wireframe"] = &wireframe;
|
||||
actions.render_mode_flags["particle_trails"] = &uses_particle_trails;
|
||||
actions.render_mode_flags["world_vertex_coords"] = &uses_world_coordinates;
|
||||
|
||||
actions.usage_flag_pointers["ALPHA"] = &uses_alpha;
|
||||
actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip;
|
||||
actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip;
|
||||
actions.usage_flag_pointers["ALPHA_ANTIALIASING_EDGE"] = &uses_alpha_antialiasing;
|
||||
actions.usage_flag_pointers["ALPHA_TEXTURE_COORDINATE"] = &uses_alpha_antialiasing;
|
||||
actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha;
|
||||
|
||||
actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss;
|
||||
actions.usage_flag_pointers["SSS_TRANSMITTANCE_DEPTH"] = &uses_transmittance;
|
||||
|
||||
actions.usage_flag_pointers["DISCARD"] = &uses_discard;
|
||||
actions.usage_flag_pointers["TIME"] = &uses_time;
|
||||
actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
|
||||
actions.usage_flag_pointers["NORMAL"] = &uses_normal;
|
||||
actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map;
|
||||
|
||||
actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent;
|
||||
actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent;
|
||||
|
||||
actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
|
||||
actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
|
||||
|
||||
actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection;
|
||||
actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection;
|
||||
actions.write_flag_pointers["VERTEX"] = &uses_vertex;
|
||||
|
||||
actions.uniforms = &uniforms;
|
||||
|
||||
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
|
||||
|
||||
Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
|
||||
|
||||
if (version.is_null()) {
|
||||
version = shader_singleton->shader.version_create();
|
||||
}
|
||||
|
||||
depth_draw = DepthDraw(depth_drawi);
|
||||
depth_test = DepthTest(depth_testi);
|
||||
uses_vertex_time = gen_code.uses_vertex_time;
|
||||
uses_fragment_time = gen_code.uses_fragment_time;
|
||||
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
|
||||
uses_screen_texture = gen_code.uses_screen_texture;
|
||||
uses_depth_texture = gen_code.uses_depth_texture;
|
||||
uses_normal_texture = gen_code.uses_normal_roughness_texture;
|
||||
uses_normal |= uses_normal_map;
|
||||
uses_tangent |= uses_normal_map;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (uses_sss) {
|
||||
WARN_PRINT_ONCE_ED("Sub-surface scattering is only available when using the Forward+ rendering backend.");
|
||||
}
|
||||
|
||||
if (uses_transmittance) {
|
||||
WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
print_line("**compiling shader:");
|
||||
print_line("**defines:\n");
|
||||
for (int i = 0; i < gen_code.defines.size(); i++) {
|
||||
print_line(gen_code.defines[i]);
|
||||
}
|
||||
|
||||
HashMap<String, String>::Iterator el = gen_code.code.begin();
|
||||
while (el) {
|
||||
print_line("\n**code " + el->key + ":\n" + el->value);
|
||||
++el;
|
||||
}
|
||||
|
||||
print_line("\n**uniforms:\n" + gen_code.uniforms);
|
||||
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
|
||||
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
|
||||
#endif
|
||||
|
||||
shader_singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
|
||||
ERR_FAIL_COND(!shader_singleton->shader.version_is_valid(version));
|
||||
|
||||
ubo_size = gen_code.uniform_total_size;
|
||||
ubo_offsets = gen_code.uniform_offsets;
|
||||
texture_uniforms = gen_code.texture_uniforms;
|
||||
|
||||
//blend modes
|
||||
|
||||
// if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
|
||||
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
|
||||
blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
|
||||
}
|
||||
|
||||
RD::PipelineColorBlendState::Attachment blend_attachment;
|
||||
|
||||
switch (blend_mode) {
|
||||
case BLEND_MODE_MIX: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_ADD: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_SUB: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
|
||||
} break;
|
||||
case BLEND_MODE_MUL: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
uses_blend_alpha = true; //force alpha used because of blend
|
||||
} break;
|
||||
case BLEND_MODE_ALPHA_TO_COVERAGE: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
|
||||
} break;
|
||||
case BLEND_MODE_PREMULT_ALPHA: {
|
||||
blend_attachment.enable_blend = true;
|
||||
blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
|
||||
blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
|
||||
blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
uses_blend_alpha = true; // Force alpha used because of blend.
|
||||
} break;
|
||||
}
|
||||
|
||||
RD::PipelineColorBlendState blend_state_blend;
|
||||
blend_state_blend.attachments.push_back(blend_attachment);
|
||||
RD::PipelineColorBlendState blend_state_opaque = RD::PipelineColorBlendState::create_disabled(1);
|
||||
RD::PipelineColorBlendState blend_state_opaque_specular = RD::PipelineColorBlendState::create_disabled(2);
|
||||
RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(1);
|
||||
RD::PipelineColorBlendState blend_state_depth_normal_roughness_giprobe = RD::PipelineColorBlendState::create_disabled(2);
|
||||
|
||||
//update pipelines
|
||||
|
||||
RD::PipelineDepthStencilState depth_stencil_state;
|
||||
|
||||
if (depth_test != DEPTH_TEST_DISABLED) {
|
||||
depth_stencil_state.enable_depth_test = true;
|
||||
depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
|
||||
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CULL_VARIANT_MAX; i++) {
|
||||
RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
|
||||
{ RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
|
||||
};
|
||||
|
||||
RD::PolygonCullMode cull_mode_rd = cull_mode_rd_table[i][cull];
|
||||
|
||||
for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
|
||||
RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
|
||||
RD::RENDER_PRIMITIVE_POINTS,
|
||||
RD::RENDER_PRIMITIVE_LINES,
|
||||
RD::RENDER_PRIMITIVE_LINESTRIPS,
|
||||
RD::RENDER_PRIMITIVE_TRIANGLES,
|
||||
RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
|
||||
};
|
||||
|
||||
RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j];
|
||||
|
||||
for (int k = 0; k < SHADER_VERSION_MAX; k++) {
|
||||
if (!static_cast<SceneShaderForwardMobile *>(singleton)->shader.is_variant_enabled(k)) {
|
||||
continue;
|
||||
}
|
||||
RD::PipelineRasterizationState raster_state;
|
||||
raster_state.cull_mode = cull_mode_rd;
|
||||
raster_state.wireframe = wireframe;
|
||||
|
||||
RD::PipelineColorBlendState blend_state;
|
||||
RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
|
||||
RD::PipelineMultisampleState multisample_state;
|
||||
|
||||
if (uses_alpha || uses_blend_alpha) {
|
||||
// only allow these flags to go through if we have some form of msaa
|
||||
if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
|
||||
multisample_state.enable_alpha_to_coverage = true;
|
||||
} else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
|
||||
multisample_state.enable_alpha_to_coverage = true;
|
||||
multisample_state.enable_alpha_to_one = true;
|
||||
}
|
||||
|
||||
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
|
||||
blend_state = blend_state_blend;
|
||||
if (depth_draw == DEPTH_DRAW_OPAQUE && !uses_alpha_clip) {
|
||||
depth_stencil.enable_depth_write = false; //alpha does not draw depth
|
||||
}
|
||||
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
|
||||
//none, blend state contains nothing
|
||||
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
|
||||
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
|
||||
} else {
|
||||
pipelines[i][j][k].clear();
|
||||
continue; // do not use this version (will error if using it is attempted)
|
||||
}
|
||||
} else {
|
||||
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
|
||||
blend_state = blend_state_opaque;
|
||||
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
|
||||
//none, leave empty
|
||||
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
|
||||
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
|
||||
} else {
|
||||
// ???
|
||||
}
|
||||
}
|
||||
|
||||
RID shader_variant = shader_singleton->shader.version_get_shader(version, k);
|
||||
pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
bool SceneShaderForwardMobile::ShaderData::is_animated() const {
|
||||
return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
|
||||
}
|
||||
|
||||
bool SceneShaderForwardMobile::ShaderData::casts_shadows() const {
|
||||
bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
|
||||
bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
|
||||
bool has_alpha = has_base_alpha || uses_blend_alpha;
|
||||
|
||||
return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
|
||||
}
|
||||
|
||||
RS::ShaderNativeSourceCode SceneShaderForwardMobile::ShaderData::get_native_source_code() const {
|
||||
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
|
||||
|
||||
return shader_singleton->shader.version_get_native_source_code(version);
|
||||
}
|
||||
|
||||
SceneShaderForwardMobile::ShaderData::ShaderData() :
|
||||
shader_list_element(this) {
|
||||
}
|
||||
|
||||
SceneShaderForwardMobile::ShaderData::~ShaderData() {
|
||||
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
|
||||
ERR_FAIL_NULL(shader_singleton);
|
||||
//pipeline variants will clear themselves if shader is gone
|
||||
if (version.is_valid()) {
|
||||
shader_singleton->shader.version_free(version);
|
||||
}
|
||||
}
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *SceneShaderForwardMobile::_create_shader_func() {
|
||||
ShaderData *shader_data = memnew(ShaderData);
|
||||
singleton->shader_list.add(&shader_data->shader_list_element);
|
||||
return shader_data;
|
||||
}
|
||||
|
||||
void SceneShaderForwardMobile::MaterialData::set_render_priority(int p_priority) {
|
||||
priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits
|
||||
}
|
||||
|
||||
void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) {
|
||||
next_pass = p_pass;
|
||||
}
|
||||
|
||||
bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
|
||||
SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
|
||||
|
||||
return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true);
|
||||
}
|
||||
|
||||
SceneShaderForwardMobile::MaterialData::~MaterialData() {
|
||||
free_parameters_uniform_set(uniform_set);
|
||||
}
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *SceneShaderForwardMobile::_create_material_func(ShaderData *p_shader) {
|
||||
MaterialData *material_data = memnew(MaterialData);
|
||||
material_data->shader_data = p_shader;
|
||||
//update will happen later anyway so do nothing.
|
||||
return material_data;
|
||||
}
|
||||
|
||||
/* Scene Shader */
|
||||
|
||||
SceneShaderForwardMobile *SceneShaderForwardMobile::singleton = nullptr;
|
||||
|
||||
SceneShaderForwardMobile::SceneShaderForwardMobile() {
|
||||
// there should be only one of these, contained within our RenderForwardMobile singleton.
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
void SceneShaderForwardMobile::init(const String p_defines) {
|
||||
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
|
||||
|
||||
/* SCENE SHADER */
|
||||
|
||||
{
|
||||
Vector<String> shader_versions;
|
||||
shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS
|
||||
shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS
|
||||
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here...
|
||||
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_SHADOW_PASS_DP
|
||||
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
|
||||
|
||||
// multiview versions of our shaders
|
||||
shader_versions.push_back("\n#define USE_MULTIVIEW\n"); // SHADER_VERSION_COLOR_PASS_MULTIVIEW
|
||||
shader_versions.push_back("\n#define USE_MULTIVIEW\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW
|
||||
shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS_MULTIVIEW
|
||||
|
||||
shader.initialize(shader_versions, p_defines);
|
||||
|
||||
if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
|
||||
shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_MULTIVIEW, false);
|
||||
shader.set_variant_enabled(SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, false);
|
||||
shader.set_variant_enabled(SHADER_VERSION_SHADOW_PASS_MULTIVIEW, false);
|
||||
}
|
||||
}
|
||||
|
||||
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs);
|
||||
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs);
|
||||
|
||||
{
|
||||
//shader compiler
|
||||
ShaderCompiler::DefaultIdentifierActions actions;
|
||||
|
||||
actions.renames["MODEL_MATRIX"] = "read_model_matrix";
|
||||
actions.renames["MODEL_NORMAL_MATRIX"] = "model_normal_matrix";
|
||||
actions.renames["VIEW_MATRIX"] = "read_view_matrix";
|
||||
actions.renames["INV_VIEW_MATRIX"] = "inv_view_matrix";
|
||||
actions.renames["PROJECTION_MATRIX"] = "projection_matrix";
|
||||
actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix";
|
||||
actions.renames["MODELVIEW_MATRIX"] = "modelview";
|
||||
actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal";
|
||||
actions.renames["MAIN_CAM_INV_VIEW_MATRIX"] = "scene_data.main_cam_inv_view_matrix";
|
||||
|
||||
actions.renames["VERTEX"] = "vertex";
|
||||
actions.renames["NORMAL"] = "normal";
|
||||
actions.renames["TANGENT"] = "tangent";
|
||||
actions.renames["BINORMAL"] = "binormal";
|
||||
actions.renames["POSITION"] = "position";
|
||||
actions.renames["UV"] = "uv_interp";
|
||||
actions.renames["UV2"] = "uv2_interp";
|
||||
actions.renames["COLOR"] = "color_interp";
|
||||
actions.renames["POINT_SIZE"] = "gl_PointSize";
|
||||
actions.renames["INSTANCE_ID"] = "gl_InstanceIndex";
|
||||
actions.renames["VERTEX_ID"] = "gl_VertexIndex";
|
||||
|
||||
actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold";
|
||||
actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale";
|
||||
actions.renames["ALPHA_ANTIALIASING_EDGE"] = "alpha_antialiasing_edge";
|
||||
actions.renames["ALPHA_TEXTURE_COORDINATE"] = "alpha_texture_coordinate";
|
||||
|
||||
//builtins
|
||||
|
||||
actions.renames["TIME"] = "scene_data_block.data.time";
|
||||
actions.renames["EXPOSURE"] = "(1.0 / scene_data_block.data.emissive_exposure_normalization)";
|
||||
actions.renames["PI"] = _MKSTR(Math_PI);
|
||||
actions.renames["TAU"] = _MKSTR(Math_TAU);
|
||||
actions.renames["E"] = _MKSTR(Math_E);
|
||||
actions.renames["VIEWPORT_SIZE"] = "read_viewport_size";
|
||||
|
||||
actions.renames["FRAGCOORD"] = "gl_FragCoord";
|
||||
actions.renames["FRONT_FACING"] = "gl_FrontFacing";
|
||||
actions.renames["NORMAL_MAP"] = "normal_map";
|
||||
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
|
||||
actions.renames["ALBEDO"] = "albedo";
|
||||
actions.renames["ALPHA"] = "alpha";
|
||||
actions.renames["PREMUL_ALPHA_FACTOR"] = "premul_alpha";
|
||||
actions.renames["METALLIC"] = "metallic";
|
||||
actions.renames["SPECULAR"] = "specular";
|
||||
actions.renames["ROUGHNESS"] = "roughness";
|
||||
actions.renames["RIM"] = "rim";
|
||||
actions.renames["RIM_TINT"] = "rim_tint";
|
||||
actions.renames["CLEARCOAT"] = "clearcoat";
|
||||
actions.renames["CLEARCOAT_ROUGHNESS"] = "clearcoat_roughness";
|
||||
actions.renames["ANISOTROPY"] = "anisotropy";
|
||||
actions.renames["ANISOTROPY_FLOW"] = "anisotropy_flow";
|
||||
actions.renames["SSS_STRENGTH"] = "sss_strength";
|
||||
actions.renames["SSS_TRANSMITTANCE_COLOR"] = "transmittance_color";
|
||||
actions.renames["SSS_TRANSMITTANCE_DEPTH"] = "transmittance_depth";
|
||||
actions.renames["SSS_TRANSMITTANCE_BOOST"] = "transmittance_boost";
|
||||
actions.renames["BACKLIGHT"] = "backlight";
|
||||
actions.renames["AO"] = "ao";
|
||||
actions.renames["AO_LIGHT_AFFECT"] = "ao_light_affect";
|
||||
actions.renames["EMISSION"] = "emission";
|
||||
actions.renames["POINT_COORD"] = "gl_PointCoord";
|
||||
actions.renames["INSTANCE_CUSTOM"] = "instance_custom";
|
||||
actions.renames["SCREEN_UV"] = "screen_uv";
|
||||
actions.renames["DEPTH"] = "gl_FragDepth";
|
||||
actions.renames["FOG"] = "fog";
|
||||
actions.renames["RADIANCE"] = "custom_radiance";
|
||||
actions.renames["IRRADIANCE"] = "custom_irradiance";
|
||||
actions.renames["BONE_INDICES"] = "bone_attrib";
|
||||
actions.renames["BONE_WEIGHTS"] = "weight_attrib";
|
||||
actions.renames["CUSTOM0"] = "custom0_attrib";
|
||||
actions.renames["CUSTOM1"] = "custom1_attrib";
|
||||
actions.renames["CUSTOM2"] = "custom2_attrib";
|
||||
actions.renames["CUSTOM3"] = "custom3_attrib";
|
||||
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
|
||||
actions.renames["LIGHT_VERTEX"] = "light_vertex";
|
||||
|
||||
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
|
||||
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
|
||||
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz";
|
||||
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
|
||||
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz";
|
||||
|
||||
actions.renames["VIEW_INDEX"] = "ViewIndex";
|
||||
actions.renames["VIEW_MONO_LEFT"] = "0";
|
||||
actions.renames["VIEW_RIGHT"] = "1";
|
||||
actions.renames["EYE_OFFSET"] = "eye_offset";
|
||||
|
||||
//for light
|
||||
actions.renames["VIEW"] = "view";
|
||||
actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
|
||||
actions.renames["LIGHT_COLOR"] = "light_color";
|
||||
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
|
||||
actions.renames["LIGHT"] = "light";
|
||||
actions.renames["ATTENUATION"] = "attenuation";
|
||||
actions.renames["DIFFUSE_LIGHT"] = "diffuse_light";
|
||||
actions.renames["SPECULAR_LIGHT"] = "specular_light";
|
||||
|
||||
actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n";
|
||||
actions.usage_defines["TANGENT"] = "#define TANGENT_USED\n";
|
||||
actions.usage_defines["BINORMAL"] = "@TANGENT";
|
||||
actions.usage_defines["RIM"] = "#define LIGHT_RIM_USED\n";
|
||||
actions.usage_defines["RIM_TINT"] = "@RIM";
|
||||
actions.usage_defines["CLEARCOAT"] = "#define LIGHT_CLEARCOAT_USED\n";
|
||||
actions.usage_defines["CLEARCOAT_ROUGHNESS"] = "@CLEARCOAT";
|
||||
actions.usage_defines["ANISOTROPY"] = "#define LIGHT_ANISOTROPY_USED\n";
|
||||
actions.usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY";
|
||||
actions.usage_defines["AO"] = "#define AO_USED\n";
|
||||
actions.usage_defines["AO_LIGHT_AFFECT"] = "#define AO_USED\n";
|
||||
actions.usage_defines["UV"] = "#define UV_USED\n";
|
||||
actions.usage_defines["UV2"] = "#define UV2_USED\n";
|
||||
actions.usage_defines["BONE_INDICES"] = "#define BONES_USED\n";
|
||||
actions.usage_defines["BONE_WEIGHTS"] = "#define WEIGHTS_USED\n";
|
||||
actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n";
|
||||
actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n";
|
||||
actions.usage_defines["CUSTOM2"] = "#define CUSTOM2_USED\n";
|
||||
actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
|
||||
actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
|
||||
actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
|
||||
actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
|
||||
actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
|
||||
actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
|
||||
actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n";
|
||||
|
||||
actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n";
|
||||
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
|
||||
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
|
||||
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
|
||||
actions.usage_defines["PREMUL_ALPHA_FACTOR"] = "#define PREMUL_ALPHA_USED";
|
||||
|
||||
actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
|
||||
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
|
||||
actions.usage_defines["BACKLIGHT"] = "#define LIGHT_BACKLIGHT_USED\n";
|
||||
actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n";
|
||||
|
||||
actions.usage_defines["FOG"] = "#define CUSTOM_FOG_USED\n";
|
||||
actions.usage_defines["RADIANCE"] = "#define CUSTOM_RADIANCE_USED\n";
|
||||
actions.usage_defines["IRRADIANCE"] = "#define CUSTOM_IRRADIANCE_USED\n";
|
||||
|
||||
actions.usage_defines["MODEL_MATRIX"] = "#define MODEL_MATRIX_USED\n";
|
||||
|
||||
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
|
||||
actions.render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
|
||||
actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n";
|
||||
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
|
||||
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
|
||||
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
|
||||
actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
|
||||
|
||||
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
|
||||
if (!force_lambert) {
|
||||
actions.render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n";
|
||||
}
|
||||
|
||||
actions.render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n";
|
||||
actions.render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n";
|
||||
|
||||
actions.render_mode_defines["sss_mode_skin"] = "#define SSS_MODE_SKIN\n";
|
||||
|
||||
actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
|
||||
|
||||
actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
|
||||
actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
|
||||
actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
|
||||
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
|
||||
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
|
||||
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
|
||||
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
|
||||
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
|
||||
|
||||
actions.base_texture_binding_index = 1;
|
||||
actions.texture_layout_set = RenderForwardMobile::MATERIAL_UNIFORM_SET;
|
||||
actions.base_uniform_string = "material.";
|
||||
actions.base_varying_index = 10;
|
||||
|
||||
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
|
||||
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
|
||||
actions.global_buffer_array_variable = "global_shader_uniforms.data";
|
||||
actions.instance_uniform_index_variable = "instances.data[draw_call.instance_index].instance_uniforms_ofs";
|
||||
|
||||
actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture
|
||||
actions.check_multiview_samplers = RendererCompositorRD::get_singleton()->is_xr_enabled(); // Make sure we check sampling multiview textures.
|
||||
|
||||
compiler.initialize(actions);
|
||||
}
|
||||
|
||||
{
|
||||
//default material and shader
|
||||
default_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(default_shader);
|
||||
material_storage->shader_set_code(default_shader, R"(
|
||||
// Default 3D material shader (Mobile).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
void vertex() {
|
||||
ROUGHNESS = 0.8;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(0.6);
|
||||
ROUGHNESS = 0.8;
|
||||
METALLIC = 0.2;
|
||||
}
|
||||
)");
|
||||
default_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(default_material);
|
||||
material_storage->material_set_shader(default_material, default_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
default_shader_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS);
|
||||
|
||||
default_material_shader_ptr = md->shader_data;
|
||||
default_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
overdraw_material_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(overdraw_material_shader);
|
||||
// Use relatively low opacity so that more "layers" of overlapping objects can be distinguished.
|
||||
material_storage->shader_set_code(overdraw_material_shader, R"(
|
||||
// 3D editor Overdraw debug draw mode shader (Mobile).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
render_mode blend_add, unshaded, fog_disabled;
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(0.4, 0.8, 0.8);
|
||||
ALPHA = 0.1;
|
||||
}
|
||||
)");
|
||||
overdraw_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(overdraw_material);
|
||||
material_storage->material_set_shader(overdraw_material, overdraw_material_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(overdraw_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
overdraw_material_shader_ptr = md->shader_data;
|
||||
overdraw_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
debug_shadow_splits_material_shader = material_storage->shader_allocate();
|
||||
material_storage->shader_initialize(debug_shadow_splits_material_shader);
|
||||
// Use relatively low opacity so that more "layers" of overlapping objects can be distinguished.
|
||||
material_storage->shader_set_code(debug_shadow_splits_material_shader, R"(
|
||||
// 3D debug shadow splits mode shader (Mobile).
|
||||
|
||||
shader_type spatial;
|
||||
|
||||
render_mode debug_shadow_splits, fog_disabled;
|
||||
|
||||
void fragment() {
|
||||
ALBEDO = vec3(1.0, 1.0, 1.0);
|
||||
}
|
||||
)");
|
||||
debug_shadow_splits_material = material_storage->material_allocate();
|
||||
material_storage->material_initialize(debug_shadow_splits_material);
|
||||
material_storage->material_set_shader(debug_shadow_splits_material, debug_shadow_splits_material_shader);
|
||||
|
||||
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(debug_shadow_splits_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
|
||||
debug_shadow_splits_material_shader_ptr = md->shader_data;
|
||||
debug_shadow_splits_material_uniform_set = md->uniform_set;
|
||||
}
|
||||
|
||||
{
|
||||
default_vec4_xform_buffer = RD::get_singleton()->storage_buffer_create(256);
|
||||
Vector<RD::Uniform> uniforms;
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
||||
u.append_id(default_vec4_xform_buffer);
|
||||
u.binding = 0;
|
||||
uniforms.push_back(u);
|
||||
|
||||
default_vec4_xform_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RenderForwardMobile::TRANSFORMS_UNIFORM_SET);
|
||||
}
|
||||
{
|
||||
RD::SamplerState sampler;
|
||||
sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;
|
||||
sampler.enable_compare = true;
|
||||
sampler.compare_op = RD::COMPARE_OP_GREATER;
|
||||
shadow_sampler = RD::get_singleton()->sampler_create(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneShaderForwardMobile::set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants) {
|
||||
default_specialization_constants = p_constants;
|
||||
for (SelfList<ShaderData> *E = shader_list.first(); E; E = E->next()) {
|
||||
for (int i = 0; i < ShaderData::CULL_VARIANT_MAX; i++) {
|
||||
for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
|
||||
for (int k = 0; k < SHADER_VERSION_MAX; k++) {
|
||||
E->self()->pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneShaderForwardMobile::~SceneShaderForwardMobile() {
|
||||
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
|
||||
|
||||
RD::get_singleton()->free(default_vec4_xform_buffer);
|
||||
RD::get_singleton()->free(shadow_sampler);
|
||||
|
||||
material_storage->shader_free(overdraw_material_shader);
|
||||
material_storage->shader_free(default_shader);
|
||||
material_storage->shader_free(debug_shadow_splits_material_shader);
|
||||
|
||||
material_storage->material_free(overdraw_material);
|
||||
material_storage->material_free(default_material);
|
||||
material_storage->material_free(debug_shadow_splits_material);
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/**************************************************************************/
|
||||
/* scene_shader_forward_mobile.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SCENE_SHADER_FORWARD_MOBILE_H
|
||||
#define SCENE_SHADER_FORWARD_MOBILE_H
|
||||
|
||||
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl.gen.h"
|
||||
|
||||
namespace RendererSceneRenderImplementation {
|
||||
|
||||
class SceneShaderForwardMobile {
|
||||
private:
|
||||
static SceneShaderForwardMobile *singleton;
|
||||
|
||||
public:
|
||||
enum ShaderVersion {
|
||||
SHADER_VERSION_COLOR_PASS,
|
||||
SHADER_VERSION_LIGHTMAP_COLOR_PASS,
|
||||
SHADER_VERSION_SHADOW_PASS,
|
||||
SHADER_VERSION_SHADOW_PASS_DP,
|
||||
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
|
||||
|
||||
SHADER_VERSION_COLOR_PASS_MULTIVIEW,
|
||||
SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW,
|
||||
SHADER_VERSION_SHADOW_PASS_MULTIVIEW,
|
||||
|
||||
SHADER_VERSION_MAX
|
||||
};
|
||||
|
||||
struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
|
||||
enum BlendMode { //used internally
|
||||
BLEND_MODE_MIX,
|
||||
BLEND_MODE_ADD,
|
||||
BLEND_MODE_SUB,
|
||||
BLEND_MODE_MUL,
|
||||
BLEND_MODE_PREMULT_ALPHA,
|
||||
BLEND_MODE_ALPHA_TO_COVERAGE
|
||||
};
|
||||
|
||||
enum DepthDraw {
|
||||
DEPTH_DRAW_DISABLED,
|
||||
DEPTH_DRAW_OPAQUE,
|
||||
DEPTH_DRAW_ALWAYS
|
||||
};
|
||||
|
||||
enum DepthTest {
|
||||
DEPTH_TEST_DISABLED,
|
||||
DEPTH_TEST_ENABLED
|
||||
};
|
||||
|
||||
enum Cull {
|
||||
CULL_DISABLED,
|
||||
CULL_FRONT,
|
||||
CULL_BACK
|
||||
};
|
||||
|
||||
enum CullVariant {
|
||||
CULL_VARIANT_NORMAL,
|
||||
CULL_VARIANT_REVERSED,
|
||||
CULL_VARIANT_DOUBLE_SIDED,
|
||||
CULL_VARIANT_MAX
|
||||
|
||||
};
|
||||
|
||||
enum AlphaAntiAliasing {
|
||||
ALPHA_ANTIALIASING_OFF,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
|
||||
};
|
||||
|
||||
bool valid = false;
|
||||
RID version;
|
||||
uint64_t vertex_input_mask = 0;
|
||||
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX];
|
||||
|
||||
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size = 0;
|
||||
|
||||
String code;
|
||||
|
||||
DepthDraw depth_draw;
|
||||
DepthTest depth_test;
|
||||
|
||||
bool uses_point_size = false;
|
||||
bool uses_alpha = false;
|
||||
bool uses_blend_alpha = false;
|
||||
bool uses_alpha_clip = false;
|
||||
bool uses_alpha_antialiasing = false;
|
||||
bool uses_depth_prepass_alpha = false;
|
||||
bool uses_discard = false;
|
||||
bool uses_roughness = false;
|
||||
bool uses_normal = false;
|
||||
bool uses_tangent = false;
|
||||
bool uses_particle_trails = false;
|
||||
|
||||
bool unshaded = false;
|
||||
bool uses_vertex = false;
|
||||
bool uses_sss = false;
|
||||
bool uses_transmittance = false;
|
||||
bool uses_screen_texture = false;
|
||||
bool uses_depth_texture = false;
|
||||
bool uses_normal_texture = false;
|
||||
bool uses_screen_texture_mipmaps = false;
|
||||
bool uses_time = false;
|
||||
bool uses_vertex_time = false;
|
||||
bool uses_fragment_time = false;
|
||||
bool writes_modelview_or_projection = false;
|
||||
bool uses_world_coordinates = false;
|
||||
|
||||
uint64_t last_pass = 0;
|
||||
uint32_t index = 0;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
|
||||
|
||||
SelfList<ShaderData> shader_list_element;
|
||||
|
||||
ShaderData();
|
||||
virtual ~ShaderData();
|
||||
};
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *_create_shader_func();
|
||||
static RendererRD::MaterialStorage::ShaderData *_create_shader_funcs() {
|
||||
return static_cast<SceneShaderForwardMobile *>(singleton)->_create_shader_func();
|
||||
}
|
||||
|
||||
struct MaterialData : public RendererRD::MaterialStorage::MaterialData {
|
||||
ShaderData *shader_data = nullptr;
|
||||
RID uniform_set;
|
||||
uint64_t last_pass = 0;
|
||||
uint32_t index = 0;
|
||||
RID next_pass;
|
||||
uint8_t priority;
|
||||
virtual void set_render_priority(int p_priority);
|
||||
virtual void set_next_pass(RID p_pass);
|
||||
virtual bool update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~MaterialData();
|
||||
};
|
||||
|
||||
SelfList<ShaderData>::List shader_list;
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *_create_material_func(ShaderData *p_shader);
|
||||
static RendererRD::MaterialStorage::MaterialData *_create_material_funcs(RendererRD::MaterialStorage::ShaderData *p_shader) {
|
||||
return static_cast<SceneShaderForwardMobile *>(singleton)->_create_material_func(static_cast<ShaderData *>(p_shader));
|
||||
}
|
||||
|
||||
SceneForwardMobileShaderRD shader;
|
||||
ShaderCompiler compiler;
|
||||
|
||||
RID default_shader;
|
||||
RID default_material;
|
||||
RID overdraw_material_shader;
|
||||
RID overdraw_material;
|
||||
RID debug_shadow_splits_material_shader;
|
||||
RID debug_shadow_splits_material;
|
||||
RID default_shader_rd;
|
||||
|
||||
RID default_vec4_xform_buffer;
|
||||
RID default_vec4_xform_uniform_set;
|
||||
|
||||
RID shadow_sampler;
|
||||
|
||||
RID default_material_uniform_set;
|
||||
ShaderData *default_material_shader_ptr = nullptr;
|
||||
|
||||
RID overdraw_material_uniform_set;
|
||||
ShaderData *overdraw_material_shader_ptr = nullptr;
|
||||
|
||||
RID debug_shadow_splits_material_uniform_set;
|
||||
ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
|
||||
|
||||
SceneShaderForwardMobile();
|
||||
~SceneShaderForwardMobile();
|
||||
|
||||
Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
|
||||
|
||||
void init(const String p_defines);
|
||||
void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
|
||||
};
|
||||
|
||||
} // namespace RendererSceneRenderImplementation
|
||||
|
||||
#endif // SCENE_SHADER_FORWARD_MOBILE_H
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/**************************************************************************/
|
||||
/* framebuffer_cache_rd.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 "framebuffer_cache_rd.h"
|
||||
|
||||
FramebufferCacheRD *FramebufferCacheRD::singleton = nullptr;
|
||||
|
||||
void FramebufferCacheRD::_bind_methods() {
|
||||
ClassDB::bind_static_method("FramebufferCacheRD", D_METHOD("get_cache_multipass", "textures", "passes", "views"), &FramebufferCacheRD::get_cache_multipass_array);
|
||||
}
|
||||
|
||||
void FramebufferCacheRD::_invalidate(Cache *p_cache) {
|
||||
if (p_cache->prev) {
|
||||
p_cache->prev->next = p_cache->next;
|
||||
} else {
|
||||
// At beginning of table
|
||||
uint32_t table_idx = p_cache->hash % HASH_TABLE_SIZE;
|
||||
hash_table[table_idx] = p_cache->next;
|
||||
}
|
||||
|
||||
if (p_cache->next) {
|
||||
p_cache->next->prev = p_cache->prev;
|
||||
}
|
||||
|
||||
cache_allocator.free(p_cache);
|
||||
cache_instances_used--;
|
||||
}
|
||||
void FramebufferCacheRD::_framebuffer_invalidation_callback(void *p_userdata) {
|
||||
singleton->_invalidate(reinterpret_cast<Cache *>(p_userdata));
|
||||
}
|
||||
|
||||
RID FramebufferCacheRD::get_cache_multipass_array(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_views) {
|
||||
Vector<RID> textures;
|
||||
Vector<RD::FramebufferPass> passes;
|
||||
|
||||
for (int i = 0; i < p_textures.size(); i++) {
|
||||
RID texture = p_textures[i];
|
||||
textures.push_back(texture); // store even if NULL
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_passes.size(); i++) {
|
||||
Ref<RDFramebufferPass> pass = p_passes[i];
|
||||
if (pass.is_valid()) {
|
||||
passes.push_back(pass->base);
|
||||
}
|
||||
}
|
||||
|
||||
return FramebufferCacheRD::get_singleton()->get_cache_multipass(textures, passes, p_views);
|
||||
}
|
||||
|
||||
FramebufferCacheRD::FramebufferCacheRD() {
|
||||
ERR_FAIL_COND(singleton != nullptr);
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
FramebufferCacheRD::~FramebufferCacheRD() {
|
||||
if (cache_instances_used > 0) {
|
||||
ERR_PRINT("At exit: " + itos(cache_instances_used) + " framebuffer cache instance(s) still in use.");
|
||||
}
|
||||
}
|
||||
316
engine/servers/rendering/renderer_rd/framebuffer_cache_rd.h
Normal file
316
engine/servers/rendering/renderer_rd/framebuffer_cache_rd.h
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/**************************************************************************/
|
||||
/* framebuffer_cache_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FRAMEBUFFER_CACHE_RD_H
|
||||
#define FRAMEBUFFER_CACHE_RD_H
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/rendering_device_binds.h"
|
||||
|
||||
class FramebufferCacheRD : public Object {
|
||||
GDCLASS(FramebufferCacheRD, Object)
|
||||
|
||||
struct Cache {
|
||||
Cache *prev = nullptr;
|
||||
Cache *next = nullptr;
|
||||
uint32_t hash = 0;
|
||||
RID cache;
|
||||
LocalVector<RID> textures;
|
||||
LocalVector<RD::FramebufferPass> passes;
|
||||
uint32_t views = 0;
|
||||
};
|
||||
|
||||
PagedAllocator<Cache> cache_allocator;
|
||||
|
||||
enum {
|
||||
HASH_TABLE_SIZE = 16381 // Prime
|
||||
};
|
||||
|
||||
Cache *hash_table[HASH_TABLE_SIZE] = {};
|
||||
|
||||
static _FORCE_INLINE_ uint32_t _hash_pass(const RD::FramebufferPass &p, uint32_t h) {
|
||||
h = hash_murmur3_one_32(p.depth_attachment, h);
|
||||
h = hash_murmur3_one_32(p.vrs_attachment, h);
|
||||
|
||||
h = hash_murmur3_one_32(p.color_attachments.size(), h);
|
||||
for (int i = 0; i < p.color_attachments.size(); i++) {
|
||||
h = hash_murmur3_one_32(p.color_attachments[i], h);
|
||||
}
|
||||
|
||||
h = hash_murmur3_one_32(p.resolve_attachments.size(), h);
|
||||
for (int i = 0; i < p.resolve_attachments.size(); i++) {
|
||||
h = hash_murmur3_one_32(p.resolve_attachments[i], h);
|
||||
}
|
||||
|
||||
h = hash_murmur3_one_32(p.preserve_attachments.size(), h);
|
||||
for (int i = 0; i < p.preserve_attachments.size(); i++) {
|
||||
h = hash_murmur3_one_32(p.preserve_attachments[i], h);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ bool _compare_pass(const RD::FramebufferPass &a, const RD::FramebufferPass &b) {
|
||||
if (a.depth_attachment != b.depth_attachment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.vrs_attachment != b.vrs_attachment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.color_attachments.size() != b.color_attachments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.color_attachments.size(); i++) {
|
||||
if (a.color_attachments[i] != b.color_attachments[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (a.resolve_attachments.size() != b.resolve_attachments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.resolve_attachments.size(); i++) {
|
||||
if (a.resolve_attachments[i] != b.resolve_attachments[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (a.preserve_attachments.size() != b.preserve_attachments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.preserve_attachments.size(); i++) {
|
||||
if (a.preserve_attachments[i] != b.preserve_attachments[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t _hash_rids(uint32_t h, const RID &arg) {
|
||||
return hash_murmur3_one_64(arg.get_id(), h);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
uint32_t _hash_rids(uint32_t h, const RID &arg, Args... args) {
|
||||
h = hash_murmur3_one_64(arg.get_id(), h);
|
||||
return _hash_rids(h, args...);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg) {
|
||||
return textures[idx] == arg;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
_FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg, Args... args) {
|
||||
if (textures[idx] != arg) {
|
||||
return false;
|
||||
}
|
||||
return _compare_args(idx + 1, textures, args...);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg) {
|
||||
textures.push_back(arg);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
_FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg, Args... args) {
|
||||
textures.push_back(arg);
|
||||
_create_args(textures, args...);
|
||||
}
|
||||
|
||||
static FramebufferCacheRD *singleton;
|
||||
|
||||
uint32_t cache_instances_used = 0;
|
||||
|
||||
void _invalidate(Cache *p_cache);
|
||||
static void _framebuffer_invalidation_callback(void *p_userdata);
|
||||
|
||||
RID _allocate_from_data(uint32_t p_views, uint32_t p_hash, uint32_t p_table_idx, const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes) {
|
||||
RID rid;
|
||||
if (p_passes.size()) {
|
||||
rid = RD::get_singleton()->framebuffer_create_multipass(p_textures, p_passes, RD::INVALID_ID, p_views);
|
||||
} else {
|
||||
rid = RD::get_singleton()->framebuffer_create(p_textures, RD::INVALID_ID, p_views);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(rid.is_null(), rid);
|
||||
|
||||
Cache *c = cache_allocator.alloc();
|
||||
c->views = p_views;
|
||||
c->cache = rid;
|
||||
c->hash = p_hash;
|
||||
c->textures.resize(p_textures.size());
|
||||
for (uint32_t i = 0; i < c->textures.size(); i++) {
|
||||
c->textures[i] = p_textures[i];
|
||||
}
|
||||
c->passes.resize(p_passes.size());
|
||||
for (uint32_t i = 0; i < c->passes.size(); i++) {
|
||||
c->passes[i] = p_passes[i];
|
||||
}
|
||||
c->prev = nullptr;
|
||||
c->next = hash_table[p_table_idx];
|
||||
if (hash_table[p_table_idx]) {
|
||||
hash_table[p_table_idx]->prev = c;
|
||||
}
|
||||
hash_table[p_table_idx] = c;
|
||||
|
||||
RD::get_singleton()->framebuffer_set_invalidation_callback(rid, _framebuffer_invalidation_callback, c);
|
||||
|
||||
cache_instances_used++;
|
||||
|
||||
return rid;
|
||||
}
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
RID get_cache(Args... args) {
|
||||
uint32_t h = hash_murmur3_one_32(1); //1 view
|
||||
h = hash_murmur3_one_32(sizeof...(Args), h);
|
||||
h = _hash_rids(h, args...);
|
||||
h = hash_murmur3_one_32(0, h); // 0 passes
|
||||
h = hash_fmix32(h);
|
||||
|
||||
uint32_t table_idx = h % HASH_TABLE_SIZE;
|
||||
{
|
||||
const Cache *c = hash_table[table_idx];
|
||||
|
||||
while (c) {
|
||||
if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == 1 && _compare_args(0, c->textures, args...)) {
|
||||
return c->cache;
|
||||
}
|
||||
c = c->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Not in cache, create:
|
||||
|
||||
Vector<RID> textures;
|
||||
_create_args(textures, args...);
|
||||
|
||||
return _allocate_from_data(1, h, table_idx, textures, Vector<RD::FramebufferPass>());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
RID get_cache_multiview(uint32_t p_views, Args... args) {
|
||||
uint32_t h = hash_murmur3_one_32(p_views);
|
||||
h = hash_murmur3_one_32(sizeof...(Args), h);
|
||||
h = _hash_rids(h, args...);
|
||||
h = hash_murmur3_one_32(0, h); // 0 passes
|
||||
h = hash_fmix32(h);
|
||||
|
||||
uint32_t table_idx = h % HASH_TABLE_SIZE;
|
||||
{
|
||||
const Cache *c = hash_table[table_idx];
|
||||
|
||||
while (c) {
|
||||
if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == p_views && _compare_args(0, c->textures, args...)) {
|
||||
return c->cache;
|
||||
}
|
||||
c = c->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Not in cache, create:
|
||||
|
||||
Vector<RID> textures;
|
||||
_create_args(textures, args...);
|
||||
|
||||
return _allocate_from_data(p_views, h, table_idx, textures, Vector<RD::FramebufferPass>());
|
||||
}
|
||||
|
||||
RID get_cache_multipass(const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes, uint32_t p_views = 1) {
|
||||
uint32_t h = hash_murmur3_one_32(p_views);
|
||||
h = hash_murmur3_one_32(p_textures.size(), h);
|
||||
for (int i = 0; i < p_textures.size(); i++) {
|
||||
h = hash_murmur3_one_64(p_textures[i].get_id(), h);
|
||||
}
|
||||
h = hash_murmur3_one_32(p_passes.size(), h);
|
||||
for (int i = 0; i < p_passes.size(); i++) {
|
||||
h = _hash_pass(p_passes[i], h);
|
||||
}
|
||||
|
||||
h = hash_fmix32(h);
|
||||
|
||||
uint32_t table_idx = h % HASH_TABLE_SIZE;
|
||||
{
|
||||
const Cache *c = hash_table[table_idx];
|
||||
|
||||
while (c) {
|
||||
if (c->hash == h && c->views == p_views && c->textures.size() == (uint32_t)p_textures.size() && c->passes.size() == (uint32_t)p_passes.size()) {
|
||||
bool all_ok = true;
|
||||
|
||||
for (int i = 0; i < p_textures.size(); i++) {
|
||||
if (p_textures[i] != c->textures[i]) {
|
||||
all_ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_ok) {
|
||||
for (int i = 0; i < p_passes.size(); i++) {
|
||||
if (!_compare_pass(p_passes[i], c->passes[i])) {
|
||||
all_ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_ok) {
|
||||
return c->cache;
|
||||
}
|
||||
}
|
||||
c = c->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Not in cache, create:
|
||||
return _allocate_from_data(p_views, h, table_idx, p_textures, p_passes);
|
||||
}
|
||||
|
||||
static RID get_cache_multipass_array(const TypedArray<RID> &p_textures, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_views = 1);
|
||||
|
||||
static FramebufferCacheRD *get_singleton() { return singleton; }
|
||||
|
||||
FramebufferCacheRD();
|
||||
~FramebufferCacheRD();
|
||||
};
|
||||
|
||||
#endif // FRAMEBUFFER_CACHE_RD_H
|
||||
126
engine/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
Normal file
126
engine/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**************************************************************************/
|
||||
/* pipeline_cache_rd.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 "pipeline_cache_rd.h"
|
||||
|
||||
#include "core/os/memory.h"
|
||||
|
||||
RID PipelineCacheRD::_generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass, uint32_t p_bool_specializations) {
|
||||
RD::PipelineMultisampleState multisample_state_version = multisample_state;
|
||||
multisample_state_version.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_framebuffer_format_id, p_render_pass);
|
||||
|
||||
bool wireframe = p_wireframe;
|
||||
|
||||
RD::PipelineRasterizationState raster_state_version = rasterization_state;
|
||||
raster_state_version.wireframe = wireframe;
|
||||
|
||||
Vector<RD::PipelineSpecializationConstant> specialization_constants = base_specialization_constants;
|
||||
|
||||
uint32_t bool_index = 0;
|
||||
uint32_t bool_specializations = p_bool_specializations;
|
||||
while (bool_specializations) {
|
||||
if (bool_specializations & (1 << bool_index)) {
|
||||
RD::PipelineSpecializationConstant sc;
|
||||
sc.bool_value = true;
|
||||
sc.constant_id = bool_index;
|
||||
sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
|
||||
specialization_constants.push_back(sc);
|
||||
bool_specializations &= ~(1 << bool_index);
|
||||
}
|
||||
bool_index++;
|
||||
}
|
||||
|
||||
RID pipeline = RD::get_singleton()->render_pipeline_create(shader, p_framebuffer_format_id, p_vertex_format_id, render_primitive, raster_state_version, multisample_state_version, depth_stencil_state, blend_state, dynamic_state_flags, p_render_pass, specialization_constants);
|
||||
ERR_FAIL_COND_V(pipeline.is_null(), RID());
|
||||
versions = static_cast<Version *>(memrealloc(versions, sizeof(Version) * (version_count + 1)));
|
||||
versions[version_count].framebuffer_id = p_framebuffer_format_id;
|
||||
versions[version_count].vertex_id = p_vertex_format_id;
|
||||
versions[version_count].wireframe = wireframe;
|
||||
versions[version_count].pipeline = pipeline;
|
||||
versions[version_count].render_pass = p_render_pass;
|
||||
versions[version_count].bool_specializations = p_bool_specializations;
|
||||
version_count++;
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void PipelineCacheRD::_clear() {
|
||||
// TODO: Clear should probably recompile all the variants already compiled instead to avoid stalls? Needs discussion.
|
||||
if (versions) {
|
||||
for (uint32_t i = 0; i < version_count; i++) {
|
||||
//shader may be gone, so this may not be valid
|
||||
if (RD::get_singleton()->render_pipeline_is_valid(versions[i].pipeline)) {
|
||||
RD::get_singleton()->free(versions[i].pipeline);
|
||||
}
|
||||
}
|
||||
version_count = 0;
|
||||
memfree(versions);
|
||||
versions = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, const Vector<RD::PipelineSpecializationConstant> &p_base_specialization_constants) {
|
||||
ERR_FAIL_COND(p_shader.is_null());
|
||||
_clear();
|
||||
shader = p_shader;
|
||||
input_mask = 0;
|
||||
render_primitive = p_primitive;
|
||||
rasterization_state = p_rasterization_state;
|
||||
multisample_state = p_multisample;
|
||||
depth_stencil_state = p_depth_stencil_state;
|
||||
blend_state = p_blend_state;
|
||||
dynamic_state_flags = p_dynamic_state_flags;
|
||||
base_specialization_constants = p_base_specialization_constants;
|
||||
}
|
||||
void PipelineCacheRD::update_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_base_specialization_constants) {
|
||||
base_specialization_constants = p_base_specialization_constants;
|
||||
_clear();
|
||||
}
|
||||
|
||||
void PipelineCacheRD::update_shader(RID p_shader) {
|
||||
ERR_FAIL_COND(p_shader.is_null());
|
||||
_clear();
|
||||
setup(p_shader, render_primitive, rasterization_state, multisample_state, depth_stencil_state, blend_state, dynamic_state_flags);
|
||||
}
|
||||
|
||||
void PipelineCacheRD::clear() {
|
||||
_clear();
|
||||
shader = RID(); //clear shader
|
||||
input_mask = 0;
|
||||
}
|
||||
|
||||
PipelineCacheRD::PipelineCacheRD() {
|
||||
version_count = 0;
|
||||
versions = nullptr;
|
||||
input_mask = 0;
|
||||
}
|
||||
|
||||
PipelineCacheRD::~PipelineCacheRD() {
|
||||
_clear();
|
||||
}
|
||||
106
engine/servers/rendering/renderer_rd/pipeline_cache_rd.h
Normal file
106
engine/servers/rendering/renderer_rd/pipeline_cache_rd.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/**************************************************************************/
|
||||
/* pipeline_cache_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PIPELINE_CACHE_RD_H
|
||||
#define PIPELINE_CACHE_RD_H
|
||||
|
||||
#include "core/os/spin_lock.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
class PipelineCacheRD {
|
||||
SpinLock spin_lock;
|
||||
|
||||
RID shader;
|
||||
uint64_t input_mask;
|
||||
|
||||
RD::RenderPrimitive render_primitive;
|
||||
RD::PipelineRasterizationState rasterization_state;
|
||||
RD::PipelineMultisampleState multisample_state;
|
||||
RD::PipelineDepthStencilState depth_stencil_state;
|
||||
RD::PipelineColorBlendState blend_state;
|
||||
int dynamic_state_flags = 0;
|
||||
Vector<RD::PipelineSpecializationConstant> base_specialization_constants;
|
||||
|
||||
struct Version {
|
||||
RD::VertexFormatID vertex_id;
|
||||
RD::FramebufferFormatID framebuffer_id;
|
||||
uint32_t render_pass;
|
||||
bool wireframe;
|
||||
uint32_t bool_specializations;
|
||||
RID pipeline;
|
||||
};
|
||||
|
||||
Version *versions = nullptr;
|
||||
uint32_t version_count;
|
||||
|
||||
RID _generate_version(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe, uint32_t p_render_pass, uint32_t p_bool_specializations = 0);
|
||||
|
||||
void _clear();
|
||||
|
||||
public:
|
||||
void setup(RID p_shader, RD::RenderPrimitive p_primitive, const RD::PipelineRasterizationState &p_rasterization_state, RD::PipelineMultisampleState p_multisample, const RD::PipelineDepthStencilState &p_depth_stencil_state, const RD::PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0, const Vector<RD::PipelineSpecializationConstant> &p_base_specialization_constants = Vector<RD::PipelineSpecializationConstant>());
|
||||
void update_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_base_specialization_constants);
|
||||
void update_shader(RID p_shader);
|
||||
|
||||
_FORCE_INLINE_ RID get_render_pipeline(RD::VertexFormatID p_vertex_format_id, RD::FramebufferFormatID p_framebuffer_format_id, bool p_wireframe = false, uint32_t p_render_pass = 0, uint32_t p_bool_specializations = 0) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND_V_MSG(shader.is_null(), RID(),
|
||||
"Attempted to use an unused shader variant (shader is null),");
|
||||
#endif
|
||||
|
||||
spin_lock.lock();
|
||||
p_wireframe |= rasterization_state.wireframe;
|
||||
|
||||
RID result;
|
||||
for (uint32_t i = 0; i < version_count; i++) {
|
||||
if (versions[i].vertex_id == p_vertex_format_id && versions[i].framebuffer_id == p_framebuffer_format_id && versions[i].wireframe == p_wireframe && versions[i].render_pass == p_render_pass && versions[i].bool_specializations == p_bool_specializations) {
|
||||
result = versions[i].pipeline;
|
||||
spin_lock.unlock();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = _generate_version(p_vertex_format_id, p_framebuffer_format_id, p_wireframe, p_render_pass, p_bool_specializations);
|
||||
spin_lock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint64_t get_vertex_input_mask() {
|
||||
if (input_mask == 0) {
|
||||
ERR_FAIL_COND_V(shader.is_null(), 0);
|
||||
input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);
|
||||
}
|
||||
return input_mask;
|
||||
}
|
||||
void clear();
|
||||
PipelineCacheRD();
|
||||
~PipelineCacheRD();
|
||||
};
|
||||
|
||||
#endif // PIPELINE_CACHE_RD_H
|
||||
2941
engine/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
Normal file
2941
engine/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
Normal file
File diff suppressed because it is too large
Load diff
466
engine/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
Normal file
466
engine/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
/**************************************************************************/
|
||||
/* renderer_canvas_render_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERER_CANVAS_RENDER_RD_H
|
||||
#define RENDERER_CANVAS_RENDER_RD_H
|
||||
|
||||
#include "servers/rendering/renderer_canvas_render.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/canvas.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl.gen.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/shader_compiler.h"
|
||||
|
||||
class RendererCanvasRenderRD : public RendererCanvasRender {
|
||||
enum {
|
||||
BASE_UNIFORM_SET = 0,
|
||||
MATERIAL_UNIFORM_SET = 1,
|
||||
TRANSFORMS_UNIFORM_SET = 2,
|
||||
CANVAS_TEXTURE_UNIFORM_SET = 3,
|
||||
};
|
||||
|
||||
const int SAMPLERS_BINDING_FIRST_INDEX = 10;
|
||||
|
||||
enum ShaderVariant {
|
||||
SHADER_VARIANT_QUAD,
|
||||
SHADER_VARIANT_NINEPATCH,
|
||||
SHADER_VARIANT_PRIMITIVE,
|
||||
SHADER_VARIANT_PRIMITIVE_POINTS,
|
||||
SHADER_VARIANT_ATTRIBUTES,
|
||||
SHADER_VARIANT_ATTRIBUTES_POINTS,
|
||||
SHADER_VARIANT_QUAD_LIGHT,
|
||||
SHADER_VARIANT_NINEPATCH_LIGHT,
|
||||
SHADER_VARIANT_PRIMITIVE_LIGHT,
|
||||
SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
|
||||
SHADER_VARIANT_ATTRIBUTES_LIGHT,
|
||||
SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
|
||||
SHADER_VARIANT_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
FLAGS_INSTANCING_MASK = 0x7F,
|
||||
FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
|
||||
FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
|
||||
|
||||
FLAGS_CLIP_RECT_UV = (1 << 9),
|
||||
FLAGS_TRANSPOSE_RECT = (1 << 10),
|
||||
|
||||
FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR = (1 << 11),
|
||||
|
||||
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
|
||||
|
||||
FLAGS_USE_SKELETON = (1 << 15),
|
||||
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
|
||||
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
|
||||
FLAGS_LIGHT_COUNT_SHIFT = 20,
|
||||
|
||||
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
|
||||
FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
|
||||
|
||||
FLAGS_USE_MSDF = (1 << 28),
|
||||
FLAGS_USE_LCD = (1 << 29),
|
||||
|
||||
FLAGS_FLIP_H = (1 << 30),
|
||||
FLAGS_FLIP_V = (1 << 31),
|
||||
};
|
||||
|
||||
enum {
|
||||
LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
|
||||
LIGHT_FLAGS_BLEND_SHIFT = 16,
|
||||
LIGHT_FLAGS_BLEND_MASK = (3 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16),
|
||||
LIGHT_FLAGS_HAS_SHADOW = (1 << 20),
|
||||
LIGHT_FLAGS_FILTER_SHIFT = 22
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_RENDER_ITEMS = 256 * 1024,
|
||||
MAX_LIGHT_TEXTURES = 1024,
|
||||
MAX_LIGHTS_PER_ITEM = 16,
|
||||
DEFAULT_MAX_LIGHTS_PER_RENDER = 256
|
||||
};
|
||||
|
||||
/****************/
|
||||
/**** SHADER ****/
|
||||
/****************/
|
||||
|
||||
enum PipelineVariant {
|
||||
PIPELINE_VARIANT_QUAD,
|
||||
PIPELINE_VARIANT_NINEPATCH,
|
||||
PIPELINE_VARIANT_PRIMITIVE_TRIANGLES,
|
||||
PIPELINE_VARIANT_PRIMITIVE_LINES,
|
||||
PIPELINE_VARIANT_PRIMITIVE_POINTS,
|
||||
PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES,
|
||||
PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP,
|
||||
PIPELINE_VARIANT_ATTRIBUTE_LINES,
|
||||
PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP,
|
||||
PIPELINE_VARIANT_ATTRIBUTE_POINTS,
|
||||
PIPELINE_VARIANT_QUAD_LCD_BLEND,
|
||||
PIPELINE_VARIANT_MAX
|
||||
};
|
||||
enum PipelineLightMode {
|
||||
PIPELINE_LIGHT_MODE_DISABLED,
|
||||
PIPELINE_LIGHT_MODE_ENABLED,
|
||||
PIPELINE_LIGHT_MODE_MAX
|
||||
};
|
||||
|
||||
struct PipelineVariants {
|
||||
PipelineCacheRD variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX];
|
||||
};
|
||||
|
||||
struct {
|
||||
CanvasShaderRD canvas_shader;
|
||||
RID default_version;
|
||||
RID default_version_rd_shader;
|
||||
RID quad_index_buffer;
|
||||
RID quad_index_array;
|
||||
PipelineVariants pipeline_variants;
|
||||
|
||||
ShaderCompiler compiler;
|
||||
} shader;
|
||||
|
||||
struct CanvasShaderData : public RendererRD::MaterialStorage::ShaderData {
|
||||
enum BlendMode { //used internally
|
||||
BLEND_MODE_MIX,
|
||||
BLEND_MODE_ADD,
|
||||
BLEND_MODE_SUB,
|
||||
BLEND_MODE_MUL,
|
||||
BLEND_MODE_PMALPHA,
|
||||
BLEND_MODE_DISABLED,
|
||||
};
|
||||
|
||||
bool valid = false;
|
||||
RID version;
|
||||
PipelineVariants pipeline_variants;
|
||||
|
||||
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
|
||||
|
||||
Vector<uint32_t> ubo_offsets;
|
||||
uint32_t ubo_size = 0;
|
||||
|
||||
String code;
|
||||
|
||||
bool uses_screen_texture = false;
|
||||
bool uses_screen_texture_mipmaps = false;
|
||||
bool uses_sdf = false;
|
||||
bool uses_time = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual bool is_animated() const;
|
||||
virtual bool casts_shadows() const;
|
||||
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
|
||||
|
||||
CanvasShaderData() {}
|
||||
virtual ~CanvasShaderData();
|
||||
};
|
||||
|
||||
RendererRD::MaterialStorage::ShaderData *_create_shader_func();
|
||||
static RendererRD::MaterialStorage::ShaderData *_create_shader_funcs() {
|
||||
return static_cast<RendererCanvasRenderRD *>(singleton)->_create_shader_func();
|
||||
}
|
||||
|
||||
struct CanvasMaterialData : public RendererRD::MaterialStorage::MaterialData {
|
||||
CanvasShaderData *shader_data = nullptr;
|
||||
RID uniform_set;
|
||||
RID uniform_set_srgb;
|
||||
|
||||
virtual void set_render_priority(int p_priority) {}
|
||||
virtual void set_next_pass(RID p_pass) {}
|
||||
virtual bool update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
|
||||
virtual ~CanvasMaterialData();
|
||||
};
|
||||
|
||||
RendererRD::MaterialStorage::MaterialData *_create_material_func(CanvasShaderData *p_shader);
|
||||
static RendererRD::MaterialStorage::MaterialData *_create_material_funcs(RendererRD::MaterialStorage::ShaderData *p_shader) {
|
||||
return static_cast<RendererCanvasRenderRD *>(singleton)->_create_material_func(static_cast<CanvasShaderData *>(p_shader));
|
||||
}
|
||||
|
||||
/**************************/
|
||||
/**** CANVAS TEXTURES *****/
|
||||
/**************************/
|
||||
|
||||
struct {
|
||||
RS::CanvasItemTextureFilter default_filter;
|
||||
RS::CanvasItemTextureRepeat default_repeat;
|
||||
} default_samplers;
|
||||
|
||||
/******************/
|
||||
/**** POLYGONS ****/
|
||||
/******************/
|
||||
|
||||
struct PolygonBuffers {
|
||||
RD::VertexFormatID vertex_format_id;
|
||||
RID vertex_buffer;
|
||||
RID vertex_array;
|
||||
RID index_buffer;
|
||||
RID indices;
|
||||
uint32_t primitive_count = 0;
|
||||
};
|
||||
|
||||
struct {
|
||||
HashMap<PolygonID, PolygonBuffers> polygons;
|
||||
PolygonID last_id;
|
||||
} polygon_buffers;
|
||||
|
||||
/********************/
|
||||
/**** PRIMITIVES ****/
|
||||
/********************/
|
||||
|
||||
struct {
|
||||
RID index_array[4];
|
||||
} primitive_arrays;
|
||||
|
||||
/*******************/
|
||||
/**** MATERIALS ****/
|
||||
/*******************/
|
||||
|
||||
/******************/
|
||||
/**** LIGHTING ****/
|
||||
/******************/
|
||||
|
||||
struct CanvasLight {
|
||||
RID texture;
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float z_far;
|
||||
float y_offset;
|
||||
Transform2D directional_xform;
|
||||
} shadow;
|
||||
};
|
||||
|
||||
RID_Owner<CanvasLight> canvas_light_owner;
|
||||
|
||||
struct ShadowRenderPushConstant {
|
||||
float projection[16];
|
||||
float modelview[8];
|
||||
float direction[2];
|
||||
float z_far;
|
||||
float pad;
|
||||
};
|
||||
|
||||
struct OccluderPolygon {
|
||||
RS::CanvasOccluderPolygonCullMode cull_mode;
|
||||
int line_point_count;
|
||||
RID vertex_buffer;
|
||||
RID vertex_array;
|
||||
RID index_buffer;
|
||||
RID index_array;
|
||||
|
||||
int sdf_point_count;
|
||||
int sdf_index_count;
|
||||
RID sdf_vertex_buffer;
|
||||
RID sdf_vertex_array;
|
||||
RID sdf_index_buffer;
|
||||
RID sdf_index_array;
|
||||
bool sdf_is_lines;
|
||||
};
|
||||
|
||||
struct LightUniform {
|
||||
float matrix[8]; //light to texture coordinate matrix
|
||||
float shadow_matrix[8]; //light to shadow coordinate matrix
|
||||
float color[4];
|
||||
|
||||
uint8_t shadow_color[4];
|
||||
uint32_t flags; //index to light texture
|
||||
float shadow_pixel_size;
|
||||
float height;
|
||||
|
||||
float position[2];
|
||||
float shadow_z_far_inv;
|
||||
float shadow_y_ofs;
|
||||
|
||||
float atlas_rect[4];
|
||||
};
|
||||
|
||||
RID_Owner<OccluderPolygon> occluder_polygon_owner;
|
||||
|
||||
enum ShadowRenderMode {
|
||||
SHADOW_RENDER_MODE_SHADOW,
|
||||
SHADOW_RENDER_MODE_SDF,
|
||||
};
|
||||
|
||||
enum {
|
||||
SHADOW_RENDER_SDF_TRIANGLES,
|
||||
SHADOW_RENDER_SDF_LINES,
|
||||
};
|
||||
|
||||
struct {
|
||||
CanvasOcclusionShaderRD shader;
|
||||
RID shader_version;
|
||||
RID render_pipelines[3];
|
||||
RID sdf_render_pipelines[2];
|
||||
RD::VertexFormatID vertex_format;
|
||||
RD::VertexFormatID sdf_vertex_format;
|
||||
RD::FramebufferFormatID framebuffer_format;
|
||||
RD::FramebufferFormatID sdf_framebuffer_format;
|
||||
} shadow_render;
|
||||
|
||||
/***************/
|
||||
/**** STATE ****/
|
||||
/***************/
|
||||
|
||||
//state that does not vary across rendering all items
|
||||
|
||||
struct State {
|
||||
//state buffer
|
||||
struct Buffer {
|
||||
float canvas_transform[16];
|
||||
float screen_transform[16];
|
||||
float canvas_normal_transform[16];
|
||||
float canvas_modulate[4];
|
||||
|
||||
float screen_pixel_size[2];
|
||||
float time;
|
||||
uint32_t use_pixel_snap;
|
||||
|
||||
float sdf_to_tex[4];
|
||||
float sdf_to_screen[2];
|
||||
float screen_to_sdf[2];
|
||||
|
||||
uint32_t directional_light_count;
|
||||
float tex_to_sdf;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
LightUniform *light_uniforms = nullptr;
|
||||
|
||||
RID lights_uniform_buffer;
|
||||
RID canvas_state_buffer;
|
||||
RID shadow_sampler;
|
||||
RID shadow_texture;
|
||||
RID shadow_depth_texture;
|
||||
RID shadow_fb;
|
||||
int shadow_texture_size = 2048;
|
||||
|
||||
RID default_transforms_uniform_set;
|
||||
|
||||
uint32_t max_lights_per_render;
|
||||
uint32_t max_lights_per_item;
|
||||
|
||||
double time;
|
||||
|
||||
} state;
|
||||
|
||||
struct PushConstant {
|
||||
float world[6];
|
||||
uint32_t flags;
|
||||
uint32_t specular_shininess;
|
||||
union {
|
||||
//rect
|
||||
struct {
|
||||
float modulation[4];
|
||||
union {
|
||||
float msdf[4];
|
||||
float ninepatch_margins[4];
|
||||
};
|
||||
float dst_rect[4];
|
||||
float src_rect[4];
|
||||
float pad[2];
|
||||
};
|
||||
//primitive
|
||||
struct {
|
||||
float points[6]; // vec2 points[3]
|
||||
float uvs[6]; // vec2 points[3]
|
||||
uint32_t colors[6]; // colors encoded as half
|
||||
};
|
||||
};
|
||||
float color_texture_pixel_size[2];
|
||||
uint32_t lights[4];
|
||||
};
|
||||
|
||||
Item *items[MAX_RENDER_ITEMS];
|
||||
|
||||
bool using_directional_lights = false;
|
||||
RID default_canvas_texture;
|
||||
|
||||
RID default_canvas_group_shader;
|
||||
RID default_canvas_group_material;
|
||||
RID default_clip_children_material;
|
||||
RID default_clip_children_shader;
|
||||
|
||||
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
|
||||
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
|
||||
|
||||
RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer);
|
||||
|
||||
bool debug_redraw = false;
|
||||
Color debug_redraw_color;
|
||||
double debug_redraw_time = 1.0;
|
||||
|
||||
inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead.
|
||||
void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
|
||||
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
|
||||
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
|
||||
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
|
||||
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4);
|
||||
|
||||
void _update_shadow_atlas();
|
||||
|
||||
public:
|
||||
PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override;
|
||||
void free_polygon(PolygonID p_polygon) override;
|
||||
|
||||
RID light_create() override;
|
||||
void light_set_texture(RID p_rid, RID p_texture) override;
|
||||
void light_set_use_shadow(RID p_rid, bool p_enable) override;
|
||||
void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override;
|
||||
void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override;
|
||||
|
||||
virtual void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override;
|
||||
|
||||
RID occluder_polygon_create() override;
|
||||
void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override;
|
||||
void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override;
|
||||
|
||||
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
|
||||
|
||||
virtual void set_shadow_texture_size(int p_size) override;
|
||||
|
||||
void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override;
|
||||
|
||||
void set_time(double p_time);
|
||||
void update() override;
|
||||
bool free(RID p_rid) override;
|
||||
RendererCanvasRenderRD();
|
||||
~RendererCanvasRenderRD();
|
||||
};
|
||||
|
||||
#endif // RENDERER_CANVAS_RENDER_RD_H
|
||||
338
engine/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
Normal file
338
engine/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
/**************************************************************************/
|
||||
/* renderer_compositor_rd.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 "renderer_compositor_rd.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
|
||||
void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) {
|
||||
Error err = RD::get_singleton()->screen_prepare_for_drawing(p_screen);
|
||||
if (err != OK) {
|
||||
// Window is minimized and does not have valid swapchain, skip drawing without printing errors.
|
||||
return;
|
||||
}
|
||||
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen);
|
||||
ERR_FAIL_COND(draw_list == RD::INVALID_ID);
|
||||
|
||||
for (int i = 0; i < p_amount; i++) {
|
||||
RID rd_texture = texture_storage->render_target_get_rd_texture(p_render_targets[i].render_target);
|
||||
ERR_CONTINUE(rd_texture.is_null());
|
||||
|
||||
if (!render_target_descriptors.has(rd_texture) || !RD::get_singleton()->uniform_set_is_valid(render_target_descriptors[rd_texture])) {
|
||||
Vector<RD::Uniform> uniforms;
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u.binding = 0;
|
||||
u.append_id(blit.sampler);
|
||||
u.append_id(rd_texture);
|
||||
uniforms.push_back(u);
|
||||
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, blit.shader.version_get_shader(blit.shader_version, BLIT_MODE_NORMAL), 0);
|
||||
|
||||
render_target_descriptors[rd_texture] = uniform_set;
|
||||
}
|
||||
|
||||
Size2 screen_size(RD::get_singleton()->screen_get_width(p_screen), RD::get_singleton()->screen_get_height(p_screen));
|
||||
BlitMode mode = p_render_targets[i].lens_distortion.apply ? BLIT_MODE_LENS : (p_render_targets[i].multi_view.use_layer ? BLIT_MODE_USE_LAYER : BLIT_MODE_NORMAL);
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[mode]);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0);
|
||||
|
||||
blit.push_constant.src_rect[0] = p_render_targets[i].src_rect.position.x;
|
||||
blit.push_constant.src_rect[1] = p_render_targets[i].src_rect.position.y;
|
||||
blit.push_constant.src_rect[2] = p_render_targets[i].src_rect.size.width;
|
||||
blit.push_constant.src_rect[3] = p_render_targets[i].src_rect.size.height;
|
||||
blit.push_constant.dst_rect[0] = p_render_targets[i].dst_rect.position.x / screen_size.width;
|
||||
blit.push_constant.dst_rect[1] = p_render_targets[i].dst_rect.position.y / screen_size.height;
|
||||
blit.push_constant.dst_rect[2] = p_render_targets[i].dst_rect.size.width / screen_size.width;
|
||||
blit.push_constant.dst_rect[3] = p_render_targets[i].dst_rect.size.height / screen_size.height;
|
||||
blit.push_constant.layer = p_render_targets[i].multi_view.layer;
|
||||
blit.push_constant.eye_center[0] = p_render_targets[i].lens_distortion.eye_center.x;
|
||||
blit.push_constant.eye_center[1] = p_render_targets[i].lens_distortion.eye_center.y;
|
||||
blit.push_constant.k1 = p_render_targets[i].lens_distortion.k1;
|
||||
blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2;
|
||||
blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
|
||||
blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
|
||||
blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
|
||||
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
|
||||
RD::get_singleton()->draw_list_draw(draw_list, true);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
void RendererCompositorRD::begin_frame(double frame_step) {
|
||||
frame++;
|
||||
delta = frame_step;
|
||||
time += frame_step;
|
||||
|
||||
double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
|
||||
time = Math::fmod(time, time_roll_over);
|
||||
|
||||
canvas->set_time(time);
|
||||
scene->set_time(time, frame_step);
|
||||
}
|
||||
|
||||
void RendererCompositorRD::end_frame(bool p_swap_buffers) {
|
||||
if (p_swap_buffers) {
|
||||
RD::get_singleton()->swap_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererCompositorRD::initialize() {
|
||||
{
|
||||
// Initialize blit
|
||||
Vector<String> blit_modes;
|
||||
blit_modes.push_back("\n");
|
||||
blit_modes.push_back("\n#define USE_LAYER\n");
|
||||
blit_modes.push_back("\n#define USE_LAYER\n#define APPLY_LENS_DISTORTION\n");
|
||||
blit_modes.push_back("\n");
|
||||
|
||||
blit.shader.initialize(blit_modes);
|
||||
|
||||
blit.shader_version = blit.shader.version_create();
|
||||
|
||||
for (int i = 0; i < BLIT_MODE_MAX; i++) {
|
||||
blit.pipelines[i] = RD::get_singleton()->render_pipeline_create(blit.shader.version_get_shader(blit.shader_version, i), RD::get_singleton()->screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), i == BLIT_MODE_NORMAL_ALPHA ? RenderingDevice::PipelineColorBlendState::create_blend() : RenderingDevice::PipelineColorBlendState::create_disabled(), 0);
|
||||
}
|
||||
|
||||
//create index array for copy shader
|
||||
Vector<uint8_t> pv;
|
||||
pv.resize(6 * 2);
|
||||
{
|
||||
uint8_t *w = pv.ptrw();
|
||||
uint16_t *p16 = (uint16_t *)w;
|
||||
p16[0] = 0;
|
||||
p16[1] = 1;
|
||||
p16[2] = 2;
|
||||
p16[3] = 0;
|
||||
p16[4] = 2;
|
||||
p16[5] = 3;
|
||||
}
|
||||
blit.index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT16, pv);
|
||||
blit.array = RD::get_singleton()->index_array_create(blit.index_buffer, 0, 6);
|
||||
|
||||
blit.sampler = RD::get_singleton()->sampler_create(RD::SamplerState());
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t RendererCompositorRD::frame = 1;
|
||||
|
||||
void RendererCompositorRD::finalize() {
|
||||
memdelete(scene);
|
||||
memdelete(canvas);
|
||||
memdelete(fog);
|
||||
memdelete(particles_storage);
|
||||
memdelete(light_storage);
|
||||
memdelete(mesh_storage);
|
||||
memdelete(material_storage);
|
||||
memdelete(texture_storage);
|
||||
memdelete(utilities);
|
||||
|
||||
//only need to erase these, the rest are erased by cascade
|
||||
blit.shader.version_free(blit.shader_version);
|
||||
RD::get_singleton()->free(blit.index_buffer);
|
||||
RD::get_singleton()->free(blit.sampler);
|
||||
}
|
||||
|
||||
void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
|
||||
if (p_image.is_null() || p_image->is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Error err = RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID);
|
||||
if (err != OK) {
|
||||
// Window is minimized and does not have valid swapchain, skip drawing without printing errors.
|
||||
return;
|
||||
}
|
||||
|
||||
RID texture = texture_storage->texture_allocate();
|
||||
texture_storage->texture_2d_initialize(texture, p_image);
|
||||
RID rd_texture = texture_storage->texture_get_rd_texture(texture, false);
|
||||
|
||||
RD::SamplerState sampler_state;
|
||||
sampler_state.min_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST;
|
||||
sampler_state.mag_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST;
|
||||
sampler_state.max_lod = 0;
|
||||
RID sampler = RD::get_singleton()->sampler_create(sampler_state);
|
||||
|
||||
RID uset;
|
||||
{
|
||||
Vector<RD::Uniform> uniforms;
|
||||
RD::Uniform u;
|
||||
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
|
||||
u.binding = 0;
|
||||
u.append_id(sampler);
|
||||
u.append_id(rd_texture);
|
||||
uniforms.push_back(u);
|
||||
uset = RD::get_singleton()->uniform_set_create(uniforms, blit.shader.version_get_shader(blit.shader_version, BLIT_MODE_NORMAL), 0);
|
||||
}
|
||||
|
||||
Size2 window_size = DisplayServer::get_singleton()->window_get_size();
|
||||
|
||||
Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height());
|
||||
Rect2 screenrect;
|
||||
if (p_scale) {
|
||||
if (window_size.width > window_size.height) {
|
||||
//scale horizontally
|
||||
screenrect.size.y = window_size.height;
|
||||
screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y;
|
||||
screenrect.position.x = (window_size.width - screenrect.size.x) / 2;
|
||||
|
||||
} else {
|
||||
//scale vertically
|
||||
screenrect.size.x = window_size.width;
|
||||
screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x;
|
||||
screenrect.position.y = (window_size.height - screenrect.size.y) / 2;
|
||||
}
|
||||
} else {
|
||||
screenrect = imgrect;
|
||||
screenrect.position += ((window_size - screenrect.size) / 2.0).floor();
|
||||
}
|
||||
|
||||
screenrect.position /= window_size;
|
||||
screenrect.size /= window_size;
|
||||
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(DisplayServer::MAIN_WINDOW_ID, p_color);
|
||||
|
||||
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[BLIT_MODE_NORMAL_ALPHA]);
|
||||
RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array);
|
||||
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uset, 0);
|
||||
|
||||
blit.push_constant.src_rect[0] = 0.0;
|
||||
blit.push_constant.src_rect[1] = 0.0;
|
||||
blit.push_constant.src_rect[2] = 1.0;
|
||||
blit.push_constant.src_rect[3] = 1.0;
|
||||
blit.push_constant.dst_rect[0] = screenrect.position.x;
|
||||
blit.push_constant.dst_rect[1] = screenrect.position.y;
|
||||
blit.push_constant.dst_rect[2] = screenrect.size.width;
|
||||
blit.push_constant.dst_rect[3] = screenrect.size.height;
|
||||
blit.push_constant.layer = 0;
|
||||
blit.push_constant.eye_center[0] = 0;
|
||||
blit.push_constant.eye_center[1] = 0;
|
||||
blit.push_constant.k1 = 0;
|
||||
blit.push_constant.k2 = 0;
|
||||
blit.push_constant.upscale = 1.0;
|
||||
blit.push_constant.aspect_ratio = 1.0;
|
||||
blit.push_constant.convert_to_srgb = false;
|
||||
|
||||
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
|
||||
RD::get_singleton()->draw_list_draw(draw_list, true);
|
||||
|
||||
RD::get_singleton()->draw_list_end();
|
||||
|
||||
RD::get_singleton()->swap_buffers();
|
||||
|
||||
texture_storage->texture_free(texture);
|
||||
RD::get_singleton()->free(sampler);
|
||||
}
|
||||
|
||||
RendererCompositorRD *RendererCompositorRD::singleton = nullptr;
|
||||
|
||||
RendererCompositorRD::RendererCompositorRD() {
|
||||
uniform_set_cache = memnew(UniformSetCacheRD);
|
||||
framebuffer_cache = memnew(FramebufferCacheRD);
|
||||
|
||||
{
|
||||
String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
|
||||
if (shader_cache_dir.is_empty()) {
|
||||
shader_cache_dir = "user://";
|
||||
}
|
||||
Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
|
||||
if (da.is_null()) {
|
||||
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
} else {
|
||||
Error err = da->change_dir("shader_cache");
|
||||
if (err != OK) {
|
||||
err = da->make_dir("shader_cache");
|
||||
}
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
} else {
|
||||
shader_cache_dir = shader_cache_dir.path_join("shader_cache");
|
||||
|
||||
bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled");
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) {
|
||||
shader_cache_dir = String(); //disable only if not editor
|
||||
}
|
||||
|
||||
if (!shader_cache_dir.is_empty()) {
|
||||
bool compress = GLOBAL_GET("rendering/shader_compiler/shader_cache/compress");
|
||||
bool use_zstd = GLOBAL_GET("rendering/shader_compiler/shader_cache/use_zstd_compression");
|
||||
bool strip_debug = GLOBAL_GET("rendering/shader_compiler/shader_cache/strip_debug");
|
||||
|
||||
ShaderRD::set_shader_cache_dir(shader_cache_dir);
|
||||
ShaderRD::set_shader_cache_save_compressed(compress);
|
||||
ShaderRD::set_shader_cache_save_compressed_zstd(use_zstd);
|
||||
ShaderRD::set_shader_cache_save_debug(!strip_debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(singleton != nullptr, "A RendererCompositorRD singleton already exists.");
|
||||
singleton = this;
|
||||
|
||||
utilities = memnew(RendererRD::Utilities);
|
||||
texture_storage = memnew(RendererRD::TextureStorage);
|
||||
material_storage = memnew(RendererRD::MaterialStorage);
|
||||
mesh_storage = memnew(RendererRD::MeshStorage);
|
||||
light_storage = memnew(RendererRD::LightStorage);
|
||||
particles_storage = memnew(RendererRD::ParticlesStorage);
|
||||
fog = memnew(RendererRD::Fog);
|
||||
canvas = memnew(RendererCanvasRenderRD());
|
||||
|
||||
String rendering_method = OS::get_singleton()->get_current_rendering_method();
|
||||
uint64_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE);
|
||||
|
||||
if (rendering_method == "mobile" || textures_per_stage < 48) {
|
||||
if (rendering_method == "forward_plus") {
|
||||
WARN_PRINT_ONCE("Platform supports less than 48 textures per stage which is less than required by the Clustered renderer. Defaulting to Mobile renderer.");
|
||||
}
|
||||
scene = memnew(RendererSceneRenderImplementation::RenderForwardMobile());
|
||||
} else if (rendering_method == "forward_plus") {
|
||||
scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered());
|
||||
} else {
|
||||
// Fall back to our high end renderer.
|
||||
ERR_PRINT(vformat("Cannot instantiate RenderingDevice-based renderer with renderer type '%s'. Defaulting to Forward+ renderer.", rendering_method));
|
||||
scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered());
|
||||
}
|
||||
|
||||
scene->init();
|
||||
}
|
||||
|
||||
RendererCompositorRD::~RendererCompositorRD() {
|
||||
singleton = nullptr;
|
||||
memdelete(uniform_set_cache);
|
||||
memdelete(framebuffer_cache);
|
||||
ShaderRD::set_shader_cache_dir(String());
|
||||
}
|
||||
151
engine/servers/rendering/renderer_rd/renderer_compositor_rd.h
Normal file
151
engine/servers/rendering/renderer_rd/renderer_compositor_rd.h
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/**************************************************************************/
|
||||
/* renderer_compositor_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERER_COMPOSITOR_RD_H
|
||||
#define RENDERER_COMPOSITOR_RD_H
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/renderer_rd/environment/fog.h"
|
||||
#include "servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h"
|
||||
#include "servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h"
|
||||
#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/renderer_canvas_render_rd.h"
|
||||
#include "servers/rendering/renderer_rd/shaders/blit.glsl.gen.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/storage_rd/mesh_storage.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/particles_storage.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/utilities.h"
|
||||
#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
|
||||
|
||||
class RendererCompositorRD : public RendererCompositor {
|
||||
protected:
|
||||
UniformSetCacheRD *uniform_set_cache = nullptr;
|
||||
FramebufferCacheRD *framebuffer_cache = nullptr;
|
||||
RendererCanvasRenderRD *canvas = nullptr;
|
||||
RendererRD::Utilities *utilities = nullptr;
|
||||
RendererRD::LightStorage *light_storage = nullptr;
|
||||
RendererRD::MaterialStorage *material_storage = nullptr;
|
||||
RendererRD::MeshStorage *mesh_storage = nullptr;
|
||||
RendererRD::ParticlesStorage *particles_storage = nullptr;
|
||||
RendererRD::TextureStorage *texture_storage = nullptr;
|
||||
RendererRD::Fog *fog = nullptr;
|
||||
RendererSceneRenderRD *scene = nullptr;
|
||||
|
||||
enum BlitMode {
|
||||
BLIT_MODE_NORMAL,
|
||||
BLIT_MODE_USE_LAYER,
|
||||
BLIT_MODE_LENS,
|
||||
BLIT_MODE_NORMAL_ALPHA,
|
||||
BLIT_MODE_MAX
|
||||
};
|
||||
|
||||
struct BlitPushConstant {
|
||||
float src_rect[4];
|
||||
float dst_rect[4];
|
||||
|
||||
float eye_center[2];
|
||||
float k1;
|
||||
float k2;
|
||||
|
||||
float upscale;
|
||||
float aspect_ratio;
|
||||
uint32_t layer;
|
||||
uint32_t convert_to_srgb;
|
||||
};
|
||||
|
||||
struct Blit {
|
||||
BlitPushConstant push_constant;
|
||||
BlitShaderRD shader;
|
||||
RID shader_version;
|
||||
RID pipelines[BLIT_MODE_MAX];
|
||||
RID index_buffer;
|
||||
RID array;
|
||||
RID sampler;
|
||||
} blit;
|
||||
|
||||
HashMap<RID, RID> render_target_descriptors;
|
||||
|
||||
double time = 0.0;
|
||||
double delta = 0.0;
|
||||
|
||||
static uint64_t frame;
|
||||
static RendererCompositorRD *singleton;
|
||||
|
||||
public:
|
||||
RendererUtilities *get_utilities() { return utilities; };
|
||||
RendererLightStorage *get_light_storage() { return light_storage; }
|
||||
RendererMaterialStorage *get_material_storage() { return material_storage; }
|
||||
RendererMeshStorage *get_mesh_storage() { return mesh_storage; }
|
||||
RendererParticlesStorage *get_particles_storage() { return particles_storage; }
|
||||
RendererTextureStorage *get_texture_storage() { return texture_storage; }
|
||||
RendererGI *get_gi() {
|
||||
ERR_FAIL_NULL_V(scene, nullptr);
|
||||
return scene->get_gi();
|
||||
}
|
||||
RendererFog *get_fog() { return fog; }
|
||||
RendererCanvasRender *get_canvas() { return canvas; }
|
||||
RendererSceneRender *get_scene() { return scene; }
|
||||
|
||||
void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter);
|
||||
|
||||
void initialize();
|
||||
void begin_frame(double frame_step);
|
||||
void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount);
|
||||
|
||||
void gl_end_frame(bool p_swap_buffers) {}
|
||||
void end_frame(bool p_swap_buffers);
|
||||
void finalize();
|
||||
|
||||
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
|
||||
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
|
||||
_ALWAYS_INLINE_ double get_total_time() const { return time; }
|
||||
|
||||
static Error is_viable() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
static RendererCompositor *_create_current() {
|
||||
return memnew(RendererCompositorRD);
|
||||
}
|
||||
|
||||
static void make_current() {
|
||||
_create_func = _create_current;
|
||||
low_end = false;
|
||||
}
|
||||
|
||||
static RendererCompositorRD *get_singleton() { return singleton; }
|
||||
RendererCompositorRD();
|
||||
~RendererCompositorRD();
|
||||
};
|
||||
|
||||
#endif // RENDERER_COMPOSITOR_RD_H
|
||||
1550
engine/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
Normal file
1550
engine/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
Normal file
File diff suppressed because it is too large
Load diff
341
engine/servers/rendering/renderer_rd/renderer_scene_render_rd.h
Normal file
341
engine/servers/rendering/renderer_rd/renderer_scene_render_rd.h
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/**************************************************************************/
|
||||
/* renderer_scene_render_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERER_SCENE_RENDER_RD_H
|
||||
#define RENDERER_SCENE_RENDER_RD_H
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/renderer_rd/cluster_builder_rd.h"
|
||||
#include "servers/rendering/renderer_rd/effects/bokeh_dof.h"
|
||||
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
|
||||
#include "servers/rendering/renderer_rd/effects/debug_effects.h"
|
||||
#include "servers/rendering/renderer_rd/effects/fsr.h"
|
||||
#include "servers/rendering/renderer_rd/effects/luminance.h"
|
||||
#include "servers/rendering/renderer_rd/effects/tone_mapper.h"
|
||||
#include "servers/rendering/renderer_rd/effects/vrs.h"
|
||||
#include "servers/rendering/renderer_rd/environment/fog.h"
|
||||
#include "servers/rendering/renderer_rd/environment/gi.h"
|
||||
#include "servers/rendering/renderer_rd/environment/sky.h"
|
||||
#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/light_storage.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_data_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
|
||||
#include "servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/rendering_method.h"
|
||||
|
||||
class RendererSceneRenderRD : public RendererSceneRender {
|
||||
friend RendererRD::SkyRD;
|
||||
friend RendererRD::GI;
|
||||
|
||||
protected:
|
||||
RendererRD::ForwardIDStorage *forward_id_storage = nullptr;
|
||||
RendererRD::BokehDOF *bokeh_dof = nullptr;
|
||||
RendererRD::CopyEffects *copy_effects = nullptr;
|
||||
RendererRD::DebugEffects *debug_effects = nullptr;
|
||||
RendererRD::Luminance *luminance = nullptr;
|
||||
RendererRD::ToneMapper *tone_mapper = nullptr;
|
||||
RendererRD::FSR *fsr = nullptr;
|
||||
RendererRD::VRS *vrs = nullptr;
|
||||
double time = 0.0;
|
||||
double time_step = 0.0;
|
||||
|
||||
/* ENVIRONMENT */
|
||||
|
||||
bool glow_bicubic_upscale = false;
|
||||
|
||||
bool use_physical_light_units = false;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
virtual RendererRD::ForwardIDStorage *create_forward_id_storage() { return memnew(RendererRD::ForwardIDStorage); };
|
||||
|
||||
void _update_vrs(Ref<RenderSceneBuffersRD> p_render_buffers);
|
||||
|
||||
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
|
||||
|
||||
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_color) = 0;
|
||||
virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data);
|
||||
|
||||
virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0;
|
||||
virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0;
|
||||
virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0;
|
||||
virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) = 0;
|
||||
|
||||
void _debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms);
|
||||
|
||||
virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
|
||||
virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
|
||||
|
||||
bool _needs_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi);
|
||||
void _post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi);
|
||||
|
||||
bool _compositor_effects_has_flag(const RenderDataRD *p_render_data, RS::CompositorEffectFlags p_flag, RS::CompositorEffectCallbackType p_callback_type = RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_ANY);
|
||||
bool _has_compositor_effect(RS::CompositorEffectCallbackType p_callback_type, const RenderDataRD *p_render_data);
|
||||
void _process_compositor_effects(RS::CompositorEffectCallbackType p_callback_type, const RenderDataRD *p_render_data);
|
||||
void _render_buffers_copy_screen_texture(const RenderDataRD *p_render_data);
|
||||
void _render_buffers_copy_depth_texture(const RenderDataRD *p_render_data);
|
||||
void _render_buffers_post_process_and_tonemap(const RenderDataRD *p_render_data);
|
||||
void _post_process_subpass(RID p_source_texture, RID p_framebuffer, const RenderDataRD *p_render_data);
|
||||
void _disable_clear_request(const RenderDataRD *p_render_data);
|
||||
|
||||
// needed for a single argument calls (material and uv2)
|
||||
PagedArrayPool<RenderGeometryInstance *> cull_argument_pool;
|
||||
PagedArray<RenderGeometryInstance *> cull_argument; //need this to exist
|
||||
|
||||
RendererRD::SkyRD sky;
|
||||
RendererRD::GI gi;
|
||||
|
||||
virtual void _update_shader_quality_settings() {}
|
||||
static bool _debug_draw_can_use_effects(RS::ViewportDebugDraw p_debug_draw);
|
||||
|
||||
private:
|
||||
RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED;
|
||||
static RendererSceneRenderRD *singleton;
|
||||
|
||||
/* Shadow atlas */
|
||||
RS::ShadowQuality shadows_quality = RS::SHADOW_QUALITY_MAX; //So it always updates when first set
|
||||
RS::ShadowQuality directional_shadow_quality = RS::SHADOW_QUALITY_MAX;
|
||||
float shadows_quality_radius = 1.0;
|
||||
float directional_shadow_quality_radius = 1.0;
|
||||
|
||||
float *directional_penumbra_shadow_kernel = nullptr;
|
||||
float *directional_soft_shadow_kernel = nullptr;
|
||||
float *penumbra_shadow_kernel = nullptr;
|
||||
float *soft_shadow_kernel = nullptr;
|
||||
int directional_penumbra_shadow_samples = 0;
|
||||
int directional_soft_shadow_samples = 0;
|
||||
int penumbra_shadow_samples = 0;
|
||||
int soft_shadow_samples = 0;
|
||||
RS::DecalFilter decals_filter = RS::DECAL_FILTER_LINEAR_MIPMAPS;
|
||||
RS::LightProjectorFilter light_projectors_filter = RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS;
|
||||
|
||||
/* RENDER BUFFERS */
|
||||
|
||||
/* GI */
|
||||
bool screen_space_roughness_limiter = false;
|
||||
float screen_space_roughness_limiter_amount = 0.25;
|
||||
float screen_space_roughness_limiter_limit = 0.18;
|
||||
|
||||
/* Light data */
|
||||
|
||||
uint64_t scene_pass = 0;
|
||||
|
||||
uint32_t max_cluster_elements = 512;
|
||||
|
||||
/* Volumetric Fog */
|
||||
|
||||
uint32_t volumetric_fog_size = 128;
|
||||
uint32_t volumetric_fog_depth = 128;
|
||||
bool volumetric_fog_filter_active = true;
|
||||
|
||||
public:
|
||||
static RendererSceneRenderRD *get_singleton() { return singleton; }
|
||||
|
||||
/* LIGHTING */
|
||||
|
||||
virtual void setup_added_reflection_probe(const Transform3D &p_transform, const Vector3 &p_half_size){};
|
||||
virtual void setup_added_light(const RS::LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture){};
|
||||
virtual void setup_added_decal(const Transform3D &p_transform, const Vector3 &p_half_size){};
|
||||
|
||||
/* GI */
|
||||
|
||||
RendererRD::GI *get_gi() { return &gi; }
|
||||
|
||||
/* SKY */
|
||||
|
||||
RendererRD::SkyRD *get_sky() { return &sky; }
|
||||
|
||||
/* SKY API */
|
||||
|
||||
virtual RID sky_allocate() override;
|
||||
virtual void sky_initialize(RID p_rid) override;
|
||||
|
||||
virtual void sky_set_radiance_size(RID p_sky, int p_radiance_size) override;
|
||||
virtual void sky_set_mode(RID p_sky, RS::SkyMode p_mode) override;
|
||||
virtual void sky_set_material(RID p_sky, RID p_material) override;
|
||||
virtual Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override;
|
||||
|
||||
/* ENVIRONMENT API */
|
||||
|
||||
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
|
||||
|
||||
virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override;
|
||||
virtual void environment_set_volumetric_fog_filter_active(bool p_enable) override;
|
||||
|
||||
virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
|
||||
virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
|
||||
virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override;
|
||||
|
||||
virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override;
|
||||
|
||||
_FORCE_INLINE_ bool is_using_physical_light_units() {
|
||||
return use_physical_light_units;
|
||||
}
|
||||
|
||||
/* REFLECTION PROBE */
|
||||
|
||||
virtual RID reflection_probe_create_framebuffer(RID p_color, RID p_depth);
|
||||
|
||||
/* FOG VOLUMES */
|
||||
|
||||
uint32_t get_volumetric_fog_size() const { return volumetric_fog_size; }
|
||||
uint32_t get_volumetric_fog_depth() const { return volumetric_fog_depth; }
|
||||
bool get_volumetric_fog_filter_active() const { return volumetric_fog_filter_active; }
|
||||
|
||||
virtual RID fog_volume_instance_create(RID p_fog_volume) override;
|
||||
virtual void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override;
|
||||
virtual void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override;
|
||||
virtual RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override;
|
||||
virtual Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override;
|
||||
|
||||
/* gi light probes */
|
||||
|
||||
virtual RID voxel_gi_instance_create(RID p_base) override;
|
||||
virtual void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override;
|
||||
virtual bool voxel_gi_needs_update(RID p_probe) const override;
|
||||
virtual void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects) override;
|
||||
virtual void voxel_gi_set_quality(RS::VoxelGIQuality p_quality) override { gi.voxel_gi_quality = p_quality; }
|
||||
|
||||
/* render buffers */
|
||||
|
||||
virtual float _render_buffers_get_luminance_multiplier();
|
||||
virtual RD::DataFormat _render_buffers_get_color_format();
|
||||
virtual bool _render_buffers_can_be_storage();
|
||||
virtual Ref<RenderSceneBuffers> render_buffers_create() override;
|
||||
virtual void gi_set_use_half_resolution(bool p_enable) override;
|
||||
|
||||
RID render_buffers_get_default_voxel_gi_buffer();
|
||||
|
||||
virtual void base_uniforms_changed() = 0;
|
||||
|
||||
virtual void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
|
||||
|
||||
virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
|
||||
|
||||
virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override;
|
||||
|
||||
virtual void set_scene_pass(uint64_t p_pass) override {
|
||||
scene_pass = p_pass;
|
||||
}
|
||||
_FORCE_INLINE_ uint64_t get_scene_pass() {
|
||||
return scene_pass;
|
||||
}
|
||||
|
||||
virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) override;
|
||||
virtual bool screen_space_roughness_limiter_is_active() const override;
|
||||
virtual float screen_space_roughness_limiter_get_amount() const;
|
||||
virtual float screen_space_roughness_limiter_get_limit() const;
|
||||
|
||||
virtual void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override;
|
||||
virtual void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override;
|
||||
|
||||
virtual void decals_set_filter(RS::DecalFilter p_filter) override;
|
||||
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
|
||||
|
||||
_FORCE_INLINE_ RS::ShadowQuality shadows_quality_get() const {
|
||||
return shadows_quality;
|
||||
}
|
||||
_FORCE_INLINE_ RS::ShadowQuality directional_shadow_quality_get() const {
|
||||
return directional_shadow_quality;
|
||||
}
|
||||
_FORCE_INLINE_ float shadows_quality_radius_get() const {
|
||||
return shadows_quality_radius;
|
||||
}
|
||||
_FORCE_INLINE_ float directional_shadow_quality_radius_get() const {
|
||||
return directional_shadow_quality_radius;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float *directional_penumbra_shadow_kernel_get() {
|
||||
return directional_penumbra_shadow_kernel;
|
||||
}
|
||||
_FORCE_INLINE_ float *directional_soft_shadow_kernel_get() {
|
||||
return directional_soft_shadow_kernel;
|
||||
}
|
||||
_FORCE_INLINE_ float *penumbra_shadow_kernel_get() {
|
||||
return penumbra_shadow_kernel;
|
||||
}
|
||||
_FORCE_INLINE_ float *soft_shadow_kernel_get() {
|
||||
return soft_shadow_kernel;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int directional_penumbra_shadow_samples_get() const {
|
||||
return directional_penumbra_shadow_samples;
|
||||
}
|
||||
_FORCE_INLINE_ int directional_soft_shadow_samples_get() const {
|
||||
return directional_soft_shadow_samples;
|
||||
}
|
||||
_FORCE_INLINE_ int penumbra_shadow_samples_get() const {
|
||||
return penumbra_shadow_samples;
|
||||
}
|
||||
_FORCE_INLINE_ int soft_shadow_samples_get() const {
|
||||
return soft_shadow_samples;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ RS::LightProjectorFilter light_projectors_get_filter() const {
|
||||
return light_projectors_filter;
|
||||
}
|
||||
_FORCE_INLINE_ RS::DecalFilter decals_get_filter() const {
|
||||
return decals_filter;
|
||||
}
|
||||
|
||||
int get_roughness_layers() const;
|
||||
bool is_using_radiance_cubemap_array() const;
|
||||
|
||||
virtual TypedArray<Image> bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) override;
|
||||
|
||||
virtual bool free(RID p_rid) override;
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override;
|
||||
_FORCE_INLINE_ RS::ViewportDebugDraw get_debug_draw_mode() const {
|
||||
return debug_draw;
|
||||
}
|
||||
|
||||
virtual void set_time(double p_time, double p_step) override;
|
||||
|
||||
virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override;
|
||||
|
||||
virtual bool is_vrs_supported() const;
|
||||
virtual bool is_dynamic_gi_supported() const;
|
||||
virtual bool is_volumetric_supported() const;
|
||||
virtual uint32_t get_max_elements() const;
|
||||
|
||||
void init();
|
||||
|
||||
RendererSceneRenderRD();
|
||||
~RendererSceneRenderRD();
|
||||
};
|
||||
|
||||
#endif // RENDERER_SCENE_RENDER_RD_H
|
||||
851
engine/servers/rendering/renderer_rd/shader_rd.cpp
Normal file
851
engine/servers/rendering/renderer_rd/shader_rd.cpp
Normal file
|
|
@ -0,0 +1,851 @@
|
|||
/**************************************************************************/
|
||||
/* shader_rd.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 "shader_rd.h"
|
||||
|
||||
#include "core/io/compression.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/version.h"
|
||||
#include "renderer_compositor_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#include "thirdparty/misc/smolv.h"
|
||||
|
||||
void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) {
|
||||
Vector<String> lines = String(p_code).split("\n");
|
||||
|
||||
String text;
|
||||
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
const String &l = lines[i];
|
||||
bool push_chunk = false;
|
||||
|
||||
StageTemplate::Chunk chunk;
|
||||
|
||||
if (l.begins_with("#VERSION_DEFINES")) {
|
||||
chunk.type = StageTemplate::Chunk::TYPE_VERSION_DEFINES;
|
||||
push_chunk = true;
|
||||
} else if (l.begins_with("#GLOBALS")) {
|
||||
switch (p_stage_type) {
|
||||
case STAGE_TYPE_VERTEX:
|
||||
chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS;
|
||||
break;
|
||||
case STAGE_TYPE_FRAGMENT:
|
||||
chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS;
|
||||
break;
|
||||
case STAGE_TYPE_COMPUTE:
|
||||
chunk.type = StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS;
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
push_chunk = true;
|
||||
} else if (l.begins_with("#MATERIAL_UNIFORMS")) {
|
||||
chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS;
|
||||
push_chunk = true;
|
||||
} else if (l.begins_with("#CODE")) {
|
||||
chunk.type = StageTemplate::Chunk::TYPE_CODE;
|
||||
push_chunk = true;
|
||||
chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper();
|
||||
} else {
|
||||
text += l + "\n";
|
||||
}
|
||||
|
||||
if (push_chunk) {
|
||||
if (!text.is_empty()) {
|
||||
StageTemplate::Chunk text_chunk;
|
||||
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
|
||||
text_chunk.text = text.utf8();
|
||||
stage_templates[p_stage_type].chunks.push_back(text_chunk);
|
||||
text = String();
|
||||
}
|
||||
stage_templates[p_stage_type].chunks.push_back(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (!text.is_empty()) {
|
||||
StageTemplate::Chunk text_chunk;
|
||||
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
|
||||
text_chunk.text = text.utf8();
|
||||
stage_templates[p_stage_type].chunks.push_back(text_chunk);
|
||||
text = String();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) {
|
||||
name = p_name;
|
||||
|
||||
if (p_compute_code) {
|
||||
_add_stage(p_compute_code, STAGE_TYPE_COMPUTE);
|
||||
is_compute = true;
|
||||
} else {
|
||||
is_compute = false;
|
||||
if (p_vertex_code) {
|
||||
_add_stage(p_vertex_code, STAGE_TYPE_VERTEX);
|
||||
}
|
||||
if (p_fragment_code) {
|
||||
_add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder tohash;
|
||||
tohash.append("[GodotVersionNumber]");
|
||||
tohash.append(VERSION_NUMBER);
|
||||
tohash.append("[GodotVersionHash]");
|
||||
tohash.append(VERSION_HASH);
|
||||
tohash.append("[SpirvCacheKey]");
|
||||
tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key());
|
||||
tohash.append("[BinaryCacheKey]");
|
||||
tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key());
|
||||
tohash.append("[Vertex]");
|
||||
tohash.append(p_vertex_code ? p_vertex_code : "");
|
||||
tohash.append("[Fragment]");
|
||||
tohash.append(p_fragment_code ? p_fragment_code : "");
|
||||
tohash.append("[Compute]");
|
||||
tohash.append(p_compute_code ? p_compute_code : "");
|
||||
|
||||
base_sha256 = tohash.as_string().sha256_text();
|
||||
}
|
||||
|
||||
RID ShaderRD::version_create() {
|
||||
//initialize() was never called
|
||||
ERR_FAIL_COND_V(group_to_variant_map.is_empty(), RID());
|
||||
|
||||
Version version;
|
||||
version.dirty = true;
|
||||
version.valid = false;
|
||||
version.initialize_needed = true;
|
||||
version.variants = nullptr;
|
||||
return version_owner.make_rid(version);
|
||||
}
|
||||
|
||||
void ShaderRD::_initialize_version(Version *p_version) {
|
||||
_clear_version(p_version);
|
||||
|
||||
p_version->valid = false;
|
||||
p_version->dirty = false;
|
||||
|
||||
p_version->variants = memnew_arr(RID, variant_defines.size());
|
||||
}
|
||||
|
||||
void ShaderRD::_clear_version(Version *p_version) {
|
||||
// Clear versions if they exist.
|
||||
if (p_version->variants) {
|
||||
for (int i = 0; i < variant_defines.size(); i++) {
|
||||
if (p_version->variants[i].is_valid()) {
|
||||
RD::get_singleton()->free(p_version->variants[i]);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete_arr(p_version->variants);
|
||||
if (p_version->variant_data) {
|
||||
memdelete_arr(p_version->variant_data);
|
||||
}
|
||||
p_version->variants = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template) {
|
||||
for (const StageTemplate::Chunk &chunk : p_template.chunks) {
|
||||
switch (chunk.type) {
|
||||
case StageTemplate::Chunk::TYPE_VERSION_DEFINES: {
|
||||
builder.append("\n"); //make sure defines begin at newline
|
||||
builder.append(general_defines.get_data());
|
||||
builder.append(variant_defines[p_variant].text.get_data());
|
||||
for (int j = 0; j < p_version->custom_defines.size(); j++) {
|
||||
builder.append(p_version->custom_defines[j].get_data());
|
||||
}
|
||||
builder.append("\n"); //make sure defines begin at newline
|
||||
if (p_version->uniforms.size()) {
|
||||
builder.append("#define MATERIAL_UNIFORMS_USED\n");
|
||||
}
|
||||
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
|
||||
builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
|
||||
}
|
||||
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
|
||||
builder.append("#define MOLTENVK_USED\n");
|
||||
#endif
|
||||
builder.append(String("#define RENDER_DRIVER_") + OS::get_singleton()->get_current_rendering_driver_name().to_upper() + "\n");
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
|
||||
builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
|
||||
builder.append(p_version->vertex_globals.get_data()); // vertex globals
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
|
||||
builder.append(p_version->fragment_globals.get_data()); // fragment globals
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_COMPUTE_GLOBALS: {
|
||||
builder.append(p_version->compute_globals.get_data()); // compute globals
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_CODE: {
|
||||
if (p_version->code_sections.has(chunk.code)) {
|
||||
builder.append(p_version->code_sections[chunk.code].get_data());
|
||||
}
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_TEXT: {
|
||||
builder.append(chunk.text.get_data());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
|
||||
uint32_t variant = group_to_variant_map[p_data->group][p_variant];
|
||||
|
||||
if (!variants_enabled[variant]) {
|
||||
return; // Variant is disabled, return.
|
||||
}
|
||||
|
||||
Vector<RD::ShaderStageSPIRVData> stages;
|
||||
|
||||
String error;
|
||||
String current_source;
|
||||
RD::ShaderStage current_stage = RD::SHADER_STAGE_VERTEX;
|
||||
bool build_ok = true;
|
||||
|
||||
if (!is_compute) {
|
||||
//vertex stage
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]);
|
||||
|
||||
current_source = builder.as_string();
|
||||
RD::ShaderStageSPIRVData stage;
|
||||
stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_VERTEX, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
|
||||
if (stage.spirv.size() == 0) {
|
||||
build_ok = false;
|
||||
} else {
|
||||
stage.shader_stage = RD::SHADER_STAGE_VERTEX;
|
||||
stages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_compute && build_ok) {
|
||||
//fragment stage
|
||||
current_stage = RD::SHADER_STAGE_FRAGMENT;
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]);
|
||||
|
||||
current_source = builder.as_string();
|
||||
RD::ShaderStageSPIRVData stage;
|
||||
stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_FRAGMENT, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
|
||||
if (stage.spirv.size() == 0) {
|
||||
build_ok = false;
|
||||
} else {
|
||||
stage.shader_stage = RD::SHADER_STAGE_FRAGMENT;
|
||||
stages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_compute) {
|
||||
//compute stage
|
||||
current_stage = RD::SHADER_STAGE_COMPUTE;
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]);
|
||||
|
||||
current_source = builder.as_string();
|
||||
|
||||
RD::ShaderStageSPIRVData stage;
|
||||
stage.spirv = RD::get_singleton()->shader_compile_spirv_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error);
|
||||
if (stage.spirv.size() == 0) {
|
||||
build_ok = false;
|
||||
} else {
|
||||
stage.shader_stage = RD::SHADER_STAGE_COMPUTE;
|
||||
stages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
if (!build_ok) {
|
||||
MutexLock lock(variant_set_mutex); //properly print the errors
|
||||
ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(variant) + " (" + variant_defines[variant].text.get_data() + ").");
|
||||
ERR_PRINT(error);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_PRINT("code:\n" + current_source.get_with_code_lines());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(variant));
|
||||
|
||||
ERR_FAIL_COND(shader_data.is_empty());
|
||||
|
||||
{
|
||||
MutexLock lock(variant_set_mutex);
|
||||
|
||||
p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]);
|
||||
p_data->version->variant_data[variant] = shader_data;
|
||||
}
|
||||
}
|
||||
|
||||
RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_version) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
RS::ShaderNativeSourceCode source_code;
|
||||
ERR_FAIL_NULL_V(version, source_code);
|
||||
|
||||
source_code.versions.resize(variant_defines.size());
|
||||
|
||||
for (int i = 0; i < source_code.versions.size(); i++) {
|
||||
if (!is_compute) {
|
||||
//vertex stage
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX]);
|
||||
|
||||
RS::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "vertex";
|
||||
stage.code = builder.as_string();
|
||||
|
||||
source_code.versions.write[i].stages.push_back(stage);
|
||||
}
|
||||
|
||||
if (!is_compute) {
|
||||
//fragment stage
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT]);
|
||||
|
||||
RS::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "fragment";
|
||||
stage.code = builder.as_string();
|
||||
|
||||
source_code.versions.write[i].stages.push_back(stage);
|
||||
}
|
||||
|
||||
if (is_compute) {
|
||||
//compute stage
|
||||
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_COMPUTE]);
|
||||
|
||||
RS::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "compute";
|
||||
stage.code = builder.as_string();
|
||||
|
||||
source_code.versions.write[i].stages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
return source_code;
|
||||
}
|
||||
|
||||
String ShaderRD::_version_get_sha1(Version *p_version) const {
|
||||
StringBuilder hash_build;
|
||||
|
||||
hash_build.append("[uniforms]");
|
||||
hash_build.append(p_version->uniforms.get_data());
|
||||
hash_build.append("[vertex_globals]");
|
||||
hash_build.append(p_version->vertex_globals.get_data());
|
||||
hash_build.append("[fragment_globals]");
|
||||
hash_build.append(p_version->fragment_globals.get_data());
|
||||
hash_build.append("[compute_globals]");
|
||||
hash_build.append(p_version->compute_globals.get_data());
|
||||
|
||||
Vector<StringName> code_sections;
|
||||
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
|
||||
code_sections.push_back(E.key);
|
||||
}
|
||||
code_sections.sort_custom<StringName::AlphCompare>();
|
||||
|
||||
for (int i = 0; i < code_sections.size(); i++) {
|
||||
hash_build.append(String("[code:") + String(code_sections[i]) + "]");
|
||||
hash_build.append(p_version->code_sections[code_sections[i]].get_data());
|
||||
}
|
||||
for (int i = 0; i < p_version->custom_defines.size(); i++) {
|
||||
hash_build.append("[custom_defines:" + itos(i) + "]");
|
||||
hash_build.append(p_version->custom_defines[i].get_data());
|
||||
}
|
||||
|
||||
return hash_build.as_string().sha1_text();
|
||||
}
|
||||
|
||||
static const char *shader_file_header = "GDSC";
|
||||
static const uint32_t cache_file_version = 3;
|
||||
|
||||
String ShaderRD::_get_cache_file_path(Version *p_version, int p_group) {
|
||||
const String &sha1 = _version_get_sha1(p_version);
|
||||
const String &api_safe_name = String(RD::get_singleton()->get_device_api_name()).validate_filename().to_lower();
|
||||
const String &path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + "." + api_safe_name + ".cache";
|
||||
return path;
|
||||
}
|
||||
|
||||
bool ShaderRD::_load_from_cache(Version *p_version, int p_group) {
|
||||
const String &path = _get_cache_file_path(p_version, p_group);
|
||||
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char header[5] = { 0, 0, 0, 0, 0 };
|
||||
f->get_buffer((uint8_t *)header, 4);
|
||||
ERR_FAIL_COND_V(header != String(shader_file_header), false);
|
||||
|
||||
uint32_t file_version = f->get_32();
|
||||
if (file_version != cache_file_version) {
|
||||
return false; // wrong version
|
||||
}
|
||||
|
||||
uint32_t variant_count = f->get_32();
|
||||
|
||||
ERR_FAIL_COND_V(variant_count != (uint32_t)group_to_variant_map[p_group].size(), false); //should not happen but check
|
||||
|
||||
for (uint32_t i = 0; i < variant_count; i++) {
|
||||
int variant_id = group_to_variant_map[p_group][i];
|
||||
uint32_t variant_size = f->get_32();
|
||||
ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[variant_id], false);
|
||||
if (!variants_enabled[variant_id]) {
|
||||
continue;
|
||||
}
|
||||
Vector<uint8_t> variant_bytes;
|
||||
variant_bytes.resize(variant_size);
|
||||
|
||||
uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
|
||||
|
||||
ERR_FAIL_COND_V(br != variant_size, false);
|
||||
|
||||
p_version->variant_data[variant_id] = variant_bytes;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < variant_count; i++) {
|
||||
int variant_id = group_to_variant_map[p_group][i];
|
||||
if (!variants_enabled[variant_id]) {
|
||||
MutexLock lock(variant_set_mutex);
|
||||
p_version->variants[variant_id] = RID();
|
||||
continue;
|
||||
}
|
||||
{
|
||||
MutexLock lock(variant_set_mutex);
|
||||
RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[variant_id], p_version->variants[variant_id]);
|
||||
if (shader.is_null()) {
|
||||
for (uint32_t j = 0; j < i; j++) {
|
||||
int variant_free_id = group_to_variant_map[p_group][j];
|
||||
RD::get_singleton()->free(p_version->variants[variant_free_id]);
|
||||
}
|
||||
ERR_FAIL_COND_V(shader.is_null(), false);
|
||||
}
|
||||
|
||||
p_version->variants[variant_id] = shader;
|
||||
}
|
||||
}
|
||||
|
||||
memdelete_arr(p_version->variant_data); //clear stages
|
||||
p_version->variant_data = nullptr;
|
||||
p_version->valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderRD::_save_to_cache(Version *p_version, int p_group) {
|
||||
ERR_FAIL_COND(!shader_cache_dir_valid);
|
||||
const String &path = _get_cache_file_path(p_version, p_group);
|
||||
Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND(f.is_null());
|
||||
f->store_buffer((const uint8_t *)shader_file_header, 4);
|
||||
f->store_32(cache_file_version); // File version.
|
||||
uint32_t variant_count = group_to_variant_map[p_group].size();
|
||||
f->store_32(variant_count); // Variant count.
|
||||
for (uint32_t i = 0; i < variant_count; i++) {
|
||||
int variant_id = group_to_variant_map[p_group][i];
|
||||
f->store_32(p_version->variant_data[variant_id].size()); // Stage count.
|
||||
f->store_buffer(p_version->variant_data[variant_id].ptr(), p_version->variant_data[variant_id].size());
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) {
|
||||
ERR_FAIL_NULL(p_version->variants);
|
||||
for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
|
||||
int variant_id = group_to_variant_map[p_group][i];
|
||||
RID shader = RD::get_singleton()->shader_create_placeholder();
|
||||
{
|
||||
MutexLock lock(variant_set_mutex);
|
||||
p_version->variants[variant_id] = shader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to compile all variants for a given group.
|
||||
// Will skip variants that are disabled.
|
||||
void ShaderRD::_compile_version(Version *p_version, int p_group) {
|
||||
if (!group_enabled[p_group]) {
|
||||
return;
|
||||
}
|
||||
|
||||
typedef Vector<uint8_t> ShaderStageData;
|
||||
p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size());
|
||||
|
||||
p_version->dirty = false;
|
||||
|
||||
if (shader_cache_dir_valid) {
|
||||
if (_load_from_cache(p_version, p_group)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CompileData compile_data;
|
||||
compile_data.version = p_version;
|
||||
compile_data.group = p_group;
|
||||
|
||||
#if 1
|
||||
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
|
||||
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
|
||||
|
||||
#else
|
||||
for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
|
||||
_compile_variant(i, &compile_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool all_valid = true;
|
||||
|
||||
for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
|
||||
int variant_id = group_to_variant_map[p_group][i];
|
||||
if (!variants_enabled[variant_id]) {
|
||||
continue; // Disabled.
|
||||
}
|
||||
if (p_version->variants[variant_id].is_null()) {
|
||||
all_valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_valid) {
|
||||
// Clear versions if they exist.
|
||||
for (int i = 0; i < variant_defines.size(); i++) {
|
||||
if (!variants_enabled[i] || !group_enabled[variant_defines[i].group]) {
|
||||
continue; // Disabled.
|
||||
}
|
||||
if (!p_version->variants[i].is_null()) {
|
||||
RD::get_singleton()->free(p_version->variants[i]);
|
||||
}
|
||||
}
|
||||
memdelete_arr(p_version->variants);
|
||||
if (p_version->variant_data) {
|
||||
memdelete_arr(p_version->variant_data);
|
||||
}
|
||||
p_version->variants = nullptr;
|
||||
p_version->variant_data = nullptr;
|
||||
return;
|
||||
} else if (shader_cache_dir_valid) {
|
||||
// Save shader cache.
|
||||
_save_to_cache(p_version, p_group);
|
||||
}
|
||||
|
||||
memdelete_arr(p_version->variant_data); //clear stages
|
||||
p_version->variant_data = nullptr;
|
||||
|
||||
p_version->valid = true;
|
||||
}
|
||||
|
||||
void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines) {
|
||||
ERR_FAIL_COND(is_compute);
|
||||
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL(version);
|
||||
version->vertex_globals = p_vertex_globals.utf8();
|
||||
version->fragment_globals = p_fragment_globals.utf8();
|
||||
version->uniforms = p_uniforms.utf8();
|
||||
version->code_sections.clear();
|
||||
for (const KeyValue<String, String> &E : p_code) {
|
||||
version->code_sections[StringName(E.key.to_upper())] = E.value.utf8();
|
||||
}
|
||||
|
||||
version->custom_defines.clear();
|
||||
for (int i = 0; i < p_custom_defines.size(); i++) {
|
||||
version->custom_defines.push_back(p_custom_defines[i].utf8());
|
||||
}
|
||||
|
||||
version->dirty = true;
|
||||
if (version->initialize_needed) {
|
||||
_initialize_version(version);
|
||||
for (int i = 0; i < group_enabled.size(); i++) {
|
||||
if (!group_enabled[i]) {
|
||||
_allocate_placeholders(version, i);
|
||||
continue;
|
||||
}
|
||||
_compile_version(version, i);
|
||||
}
|
||||
version->initialize_needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines) {
|
||||
ERR_FAIL_COND(!is_compute);
|
||||
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL(version);
|
||||
|
||||
version->compute_globals = p_compute_globals.utf8();
|
||||
version->uniforms = p_uniforms.utf8();
|
||||
|
||||
version->code_sections.clear();
|
||||
for (const KeyValue<String, String> &E : p_code) {
|
||||
version->code_sections[StringName(E.key.to_upper())] = E.value.utf8();
|
||||
}
|
||||
|
||||
version->custom_defines.clear();
|
||||
for (int i = 0; i < p_custom_defines.size(); i++) {
|
||||
version->custom_defines.push_back(p_custom_defines[i].utf8());
|
||||
}
|
||||
|
||||
version->dirty = true;
|
||||
if (version->initialize_needed) {
|
||||
_initialize_version(version);
|
||||
for (int i = 0; i < group_enabled.size(); i++) {
|
||||
if (!group_enabled[i]) {
|
||||
_allocate_placeholders(version, i);
|
||||
continue;
|
||||
}
|
||||
_compile_version(version, i);
|
||||
}
|
||||
version->initialize_needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderRD::version_is_valid(RID p_version) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL_V(version, false);
|
||||
|
||||
if (version->dirty) {
|
||||
_initialize_version(version);
|
||||
for (int i = 0; i < group_enabled.size(); i++) {
|
||||
if (!group_enabled[i]) {
|
||||
_allocate_placeholders(version, i);
|
||||
continue;
|
||||
}
|
||||
_compile_version(version, i);
|
||||
}
|
||||
}
|
||||
|
||||
return version->valid;
|
||||
}
|
||||
|
||||
bool ShaderRD::version_free(RID p_version) {
|
||||
if (version_owner.owns(p_version)) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
_clear_version(version);
|
||||
version_owner.free(p_version);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderRD::set_variant_enabled(int p_variant, bool p_enabled) {
|
||||
ERR_FAIL_COND(version_owner.get_rid_count() > 0); //versions exist
|
||||
ERR_FAIL_INDEX(p_variant, variants_enabled.size());
|
||||
variants_enabled.write[p_variant] = p_enabled;
|
||||
}
|
||||
|
||||
bool ShaderRD::is_variant_enabled(int p_variant) const {
|
||||
ERR_FAIL_INDEX_V(p_variant, variants_enabled.size(), false);
|
||||
return variants_enabled[p_variant];
|
||||
}
|
||||
|
||||
void ShaderRD::enable_group(int p_group) {
|
||||
ERR_FAIL_INDEX(p_group, group_enabled.size());
|
||||
|
||||
if (group_enabled[p_group]) {
|
||||
// Group already enabled, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
group_enabled.write[p_group] = true;
|
||||
|
||||
// Compile all versions again to include the new group.
|
||||
List<RID> all_versions;
|
||||
version_owner.get_owned_list(&all_versions);
|
||||
for (const RID &E : all_versions) {
|
||||
Version *version = version_owner.get_or_null(E);
|
||||
_compile_version(version, p_group);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderRD::is_group_enabled(int p_group) const {
|
||||
return group_enabled[p_group];
|
||||
}
|
||||
|
||||
bool ShaderRD::shader_cache_cleanup_on_start = false;
|
||||
|
||||
ShaderRD::ShaderRD() {
|
||||
// Do not feel forced to use this, in most cases it makes little to no difference.
|
||||
bool use_32_threads = false;
|
||||
if (RD::get_singleton()->get_device_vendor_name() == "NVIDIA") {
|
||||
use_32_threads = true;
|
||||
}
|
||||
String base_compute_define_text;
|
||||
if (use_32_threads) {
|
||||
base_compute_define_text = "\n#define NATIVE_LOCAL_GROUP_SIZE 32\n#define NATIVE_LOCAL_SIZE_2D_X 8\n#define NATIVE_LOCAL_SIZE_2D_Y 4\n";
|
||||
} else {
|
||||
base_compute_define_text = "\n#define NATIVE_LOCAL_GROUP_SIZE 64\n#define NATIVE_LOCAL_SIZE_2D_X 8\n#define NATIVE_LOCAL_SIZE_2D_Y 8\n";
|
||||
}
|
||||
|
||||
base_compute_defines = base_compute_define_text.ascii();
|
||||
}
|
||||
|
||||
void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String &p_general_defines) {
|
||||
ERR_FAIL_COND(variant_defines.size());
|
||||
ERR_FAIL_COND(p_variant_defines.is_empty());
|
||||
|
||||
general_defines = p_general_defines.utf8();
|
||||
|
||||
// When initialized this way, there is just one group and its always enabled.
|
||||
group_to_variant_map.insert(0, LocalVector<int>{});
|
||||
group_enabled.push_back(true);
|
||||
|
||||
for (int i = 0; i < p_variant_defines.size(); i++) {
|
||||
variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true));
|
||||
variants_enabled.push_back(true);
|
||||
group_to_variant_map[0].push_back(i);
|
||||
}
|
||||
|
||||
if (!shader_cache_dir.is_empty()) {
|
||||
group_sha256.resize(1);
|
||||
_initialize_cache();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::_initialize_cache() {
|
||||
for (const KeyValue<int, LocalVector<int>> &E : group_to_variant_map) {
|
||||
StringBuilder hash_build;
|
||||
|
||||
hash_build.append("[base_hash]");
|
||||
hash_build.append(base_sha256);
|
||||
hash_build.append("[general_defines]");
|
||||
hash_build.append(general_defines.get_data());
|
||||
hash_build.append("[group_id]");
|
||||
hash_build.append(itos(E.key));
|
||||
for (uint32_t i = 0; i < E.value.size(); i++) {
|
||||
hash_build.append("[variant_defines:" + itos(E.value[i]) + "]");
|
||||
hash_build.append(variant_defines[E.value[i]].text.get_data());
|
||||
}
|
||||
|
||||
group_sha256[E.key] = hash_build.as_string().sha256_text();
|
||||
|
||||
Ref<DirAccess> d = DirAccess::open(shader_cache_dir);
|
||||
ERR_FAIL_COND(d.is_null());
|
||||
if (d->change_dir(name) != OK) {
|
||||
Error err = d->make_dir(name);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
d->change_dir(name);
|
||||
}
|
||||
|
||||
// Erase other versions?
|
||||
if (shader_cache_cleanup_on_start) {
|
||||
}
|
||||
//
|
||||
if (d->change_dir(group_sha256[E.key]) != OK) {
|
||||
Error err = d->make_dir(group_sha256[E.key]);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
}
|
||||
shader_cache_dir_valid = true;
|
||||
|
||||
print_verbose("Shader '" + name + "' (group " + itos(E.key) + ") SHA256: " + group_sha256[E.key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but allows specifying shader compilation groups.
|
||||
void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines) {
|
||||
ERR_FAIL_COND(variant_defines.size());
|
||||
ERR_FAIL_COND(p_variant_defines.is_empty());
|
||||
|
||||
general_defines = p_general_defines.utf8();
|
||||
|
||||
int max_group_id = 0;
|
||||
|
||||
for (int i = 0; i < p_variant_defines.size(); i++) {
|
||||
// Fill variant array.
|
||||
variant_defines.push_back(p_variant_defines[i]);
|
||||
variants_enabled.push_back(true);
|
||||
|
||||
// Map variant array index to group id, so we can iterate over groups later.
|
||||
if (!group_to_variant_map.has(p_variant_defines[i].group)) {
|
||||
group_to_variant_map.insert(p_variant_defines[i].group, LocalVector<int>{});
|
||||
}
|
||||
group_to_variant_map[p_variant_defines[i].group].push_back(i);
|
||||
|
||||
// Track max size.
|
||||
if (p_variant_defines[i].group > max_group_id) {
|
||||
max_group_id = p_variant_defines[i].group;
|
||||
}
|
||||
}
|
||||
|
||||
// Set all to groups to false, then enable those that should be default.
|
||||
group_enabled.resize_zeroed(max_group_id + 1);
|
||||
bool *enabled_ptr = group_enabled.ptrw();
|
||||
for (int i = 0; i < p_variant_defines.size(); i++) {
|
||||
if (p_variant_defines[i].default_enabled) {
|
||||
enabled_ptr[p_variant_defines[i].group] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shader_cache_dir.is_empty()) {
|
||||
group_sha256.resize(max_group_id + 1);
|
||||
_initialize_cache();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderRD::set_shader_cache_dir(const String &p_dir) {
|
||||
shader_cache_dir = p_dir;
|
||||
}
|
||||
|
||||
void ShaderRD::set_shader_cache_save_compressed(bool p_enable) {
|
||||
shader_cache_save_compressed = p_enable;
|
||||
}
|
||||
|
||||
void ShaderRD::set_shader_cache_save_compressed_zstd(bool p_enable) {
|
||||
shader_cache_save_compressed_zstd = p_enable;
|
||||
}
|
||||
|
||||
void ShaderRD::set_shader_cache_save_debug(bool p_enable) {
|
||||
shader_cache_save_debug = p_enable;
|
||||
}
|
||||
|
||||
String ShaderRD::shader_cache_dir;
|
||||
bool ShaderRD::shader_cache_save_compressed = true;
|
||||
bool ShaderRD::shader_cache_save_compressed_zstd = true;
|
||||
bool ShaderRD::shader_cache_save_debug = true;
|
||||
|
||||
ShaderRD::~ShaderRD() {
|
||||
List<RID> remaining;
|
||||
version_owner.get_owned_list(&remaining);
|
||||
if (remaining.size()) {
|
||||
ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
|
||||
while (remaining.size()) {
|
||||
version_free(remaining.front()->get());
|
||||
remaining.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
211
engine/servers/rendering/renderer_rd/shader_rd.h
Normal file
211
engine/servers/rendering/renderer_rd/shader_rd.h
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/**************************************************************************/
|
||||
/* shader_rd.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SHADER_RD_H
|
||||
#define SHADER_RD_H
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/string/string_builder.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
class ShaderRD {
|
||||
public:
|
||||
struct VariantDefine {
|
||||
int group = 0;
|
||||
CharString text;
|
||||
bool default_enabled = true;
|
||||
VariantDefine(){};
|
||||
VariantDefine(int p_group, const String &p_text, bool p_default_enabled) {
|
||||
group = p_group;
|
||||
default_enabled = p_default_enabled;
|
||||
text = p_text.utf8();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
//versions
|
||||
CharString general_defines;
|
||||
Vector<VariantDefine> variant_defines;
|
||||
Vector<bool> variants_enabled;
|
||||
HashMap<int, LocalVector<int>> group_to_variant_map;
|
||||
Vector<bool> group_enabled;
|
||||
|
||||
struct Version {
|
||||
CharString uniforms;
|
||||
CharString vertex_globals;
|
||||
CharString compute_globals;
|
||||
CharString fragment_globals;
|
||||
HashMap<StringName, CharString> code_sections;
|
||||
Vector<CharString> custom_defines;
|
||||
|
||||
Vector<uint8_t> *variant_data = nullptr;
|
||||
RID *variants = nullptr; // Same size as variant defines.
|
||||
|
||||
bool valid;
|
||||
bool dirty;
|
||||
bool initialize_needed;
|
||||
};
|
||||
|
||||
Mutex variant_set_mutex;
|
||||
|
||||
struct CompileData {
|
||||
Version *version;
|
||||
int group = 0;
|
||||
};
|
||||
|
||||
void _compile_variant(uint32_t p_variant, const CompileData *p_data);
|
||||
|
||||
void _initialize_version(Version *p_version);
|
||||
void _clear_version(Version *p_version);
|
||||
void _compile_version(Version *p_version, int p_group);
|
||||
void _allocate_placeholders(Version *p_version, int p_group);
|
||||
|
||||
RID_Owner<Version> version_owner;
|
||||
|
||||
struct StageTemplate {
|
||||
struct Chunk {
|
||||
enum Type {
|
||||
TYPE_VERSION_DEFINES,
|
||||
TYPE_MATERIAL_UNIFORMS,
|
||||
TYPE_VERTEX_GLOBALS,
|
||||
TYPE_FRAGMENT_GLOBALS,
|
||||
TYPE_COMPUTE_GLOBALS,
|
||||
TYPE_CODE,
|
||||
TYPE_TEXT
|
||||
};
|
||||
|
||||
Type type;
|
||||
StringName code;
|
||||
CharString text;
|
||||
};
|
||||
LocalVector<Chunk> chunks;
|
||||
};
|
||||
|
||||
bool is_compute = false;
|
||||
|
||||
String name;
|
||||
|
||||
CharString base_compute_defines;
|
||||
|
||||
String base_sha256;
|
||||
LocalVector<String> group_sha256;
|
||||
|
||||
static String shader_cache_dir;
|
||||
static bool shader_cache_cleanup_on_start;
|
||||
static bool shader_cache_save_compressed;
|
||||
static bool shader_cache_save_compressed_zstd;
|
||||
static bool shader_cache_save_debug;
|
||||
bool shader_cache_dir_valid = false;
|
||||
|
||||
enum StageType {
|
||||
STAGE_TYPE_VERTEX,
|
||||
STAGE_TYPE_FRAGMENT,
|
||||
STAGE_TYPE_COMPUTE,
|
||||
STAGE_TYPE_MAX,
|
||||
};
|
||||
|
||||
StageTemplate stage_templates[STAGE_TYPE_MAX];
|
||||
|
||||
void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template);
|
||||
|
||||
void _add_stage(const char *p_code, StageType p_stage_type);
|
||||
|
||||
String _version_get_sha1(Version *p_version) const;
|
||||
String _get_cache_file_path(Version *p_version, int p_group);
|
||||
bool _load_from_cache(Version *p_version, int p_group);
|
||||
void _save_to_cache(Version *p_version, int p_group);
|
||||
void _initialize_cache();
|
||||
|
||||
protected:
|
||||
ShaderRD();
|
||||
void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name);
|
||||
|
||||
public:
|
||||
RID version_create();
|
||||
|
||||
void version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines);
|
||||
void version_set_compute_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_compute_globals, const Vector<String> &p_custom_defines);
|
||||
|
||||
_FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) {
|
||||
ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID());
|
||||
ERR_FAIL_COND_V(!variants_enabled[p_variant], RID());
|
||||
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL_V(version, RID());
|
||||
|
||||
if (version->dirty) {
|
||||
_initialize_version(version);
|
||||
for (int i = 0; i < group_enabled.size(); i++) {
|
||||
if (!group_enabled[i]) {
|
||||
_allocate_placeholders(version, i);
|
||||
continue;
|
||||
}
|
||||
_compile_version(version, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!version->valid) {
|
||||
return RID();
|
||||
}
|
||||
|
||||
return version->variants[p_variant];
|
||||
}
|
||||
|
||||
bool version_is_valid(RID p_version);
|
||||
|
||||
bool version_free(RID p_version);
|
||||
|
||||
// Enable/disable variants for things that you know won't be used at engine initialization time .
|
||||
void set_variant_enabled(int p_variant, bool p_enabled);
|
||||
bool is_variant_enabled(int p_variant) const;
|
||||
|
||||
// Enable/disable groups for things that might be enabled at run time.
|
||||
void enable_group(int p_group);
|
||||
bool is_group_enabled(int p_group) const;
|
||||
|
||||
static void set_shader_cache_dir(const String &p_dir);
|
||||
static void set_shader_cache_save_compressed(bool p_enable);
|
||||
static void set_shader_cache_save_compressed_zstd(bool p_enable);
|
||||
static void set_shader_cache_save_debug(bool p_enable);
|
||||
|
||||
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
|
||||
|
||||
void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
|
||||
void initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines = "");
|
||||
|
||||
virtual ~ShaderRD();
|
||||
};
|
||||
|
||||
#endif // SHADER_RD_H
|
||||
22
engine/servers/rendering/renderer_rd/shaders/SCsub
Normal file
22
engine/servers/rendering/renderer_rd/shaders/SCsub
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
||||
SConscript("effects/SCsub")
|
||||
SConscript("environment/SCsub")
|
||||
SConscript("forward_clustered/SCsub")
|
||||
SConscript("forward_mobile/SCsub")
|
||||
108
engine/servers/rendering/renderer_rd/shaders/blit.glsl
Normal file
108
engine/servers/rendering/renderer_rd/shaders/blit.glsl
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std140) uniform Pos {
|
||||
vec4 src_rect;
|
||||
vec4 dst_rect;
|
||||
|
||||
vec2 eye_center;
|
||||
float k1;
|
||||
float k2;
|
||||
|
||||
float upscale;
|
||||
float aspect_ratio;
|
||||
uint layer;
|
||||
uint pad1;
|
||||
}
|
||||
data;
|
||||
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
|
||||
uv = data.src_rect.xy + base_arr[gl_VertexIndex] * data.src_rect.zw;
|
||||
vec2 vtx = data.dst_rect.xy + base_arr[gl_VertexIndex] * data.dst_rect.zw;
|
||||
gl_Position = vec4(vtx * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std140) uniform Pos {
|
||||
vec4 src_rect;
|
||||
vec4 dst_rect;
|
||||
|
||||
vec2 eye_center;
|
||||
float k1;
|
||||
float k2;
|
||||
|
||||
float upscale;
|
||||
float aspect_ratio;
|
||||
uint layer;
|
||||
bool convert_to_srgb;
|
||||
}
|
||||
data;
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
#ifdef USE_LAYER
|
||||
layout(binding = 0) uniform sampler2DArray src_rt;
|
||||
#else
|
||||
layout(binding = 0) uniform sampler2D src_rt;
|
||||
#endif
|
||||
|
||||
vec3 linear_to_srgb(vec3 color) {
|
||||
// If going to srgb, clamp from 0 to 1.
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef APPLY_LENS_DISTORTION
|
||||
vec2 coords = uv * 2.0 - 1.0;
|
||||
vec2 offset = coords - data.eye_center;
|
||||
|
||||
// take aspect ratio into account
|
||||
offset.y /= data.aspect_ratio;
|
||||
|
||||
// distort
|
||||
vec2 offset_sq = offset * offset;
|
||||
float radius_sq = offset_sq.x + offset_sq.y;
|
||||
float radius_s4 = radius_sq * radius_sq;
|
||||
float distortion_scale = 1.0 + (data.k1 * radius_sq) + (data.k2 * radius_s4);
|
||||
offset *= distortion_scale;
|
||||
|
||||
// reapply aspect ratio
|
||||
offset.y *= data.aspect_ratio;
|
||||
|
||||
// add our eye center back in
|
||||
coords = offset + data.eye_center;
|
||||
coords /= data.upscale;
|
||||
|
||||
// and check our color
|
||||
if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
|
||||
color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
} else {
|
||||
// layer is always used here
|
||||
coords = (coords + vec2(1.0)) / vec2(2.0);
|
||||
color = texture(src_rt, vec3(coords, data.layer));
|
||||
}
|
||||
#elif defined(USE_LAYER)
|
||||
color = texture(src_rt, vec3(uv, data.layer));
|
||||
#else
|
||||
color = texture(src_rt, uv);
|
||||
#endif
|
||||
|
||||
if (data.convert_to_srgb) {
|
||||
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
|
||||
}
|
||||
}
|
||||
744
engine/servers/rendering/renderer_rd/shaders/canvas.glsl
Normal file
744
engine/servers/rendering/renderer_rd/shaders/canvas.glsl
Normal file
|
|
@ -0,0 +1,744 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_ATTRIBUTES
|
||||
layout(location = 0) in vec2 vertex_attrib;
|
||||
layout(location = 3) in vec4 color_attrib;
|
||||
layout(location = 4) in vec2 uv_attrib;
|
||||
|
||||
#if defined(CUSTOM0_USED)
|
||||
layout(location = 6) in vec4 custom0_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(CUSTOM1_USED)
|
||||
layout(location = 7) in vec4 custom1_attrib;
|
||||
#endif
|
||||
|
||||
layout(location = 10) in uvec4 bone_attrib;
|
||||
layout(location = 11) in vec4 weight_attrib;
|
||||
|
||||
#endif
|
||||
|
||||
#include "canvas_uniforms_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
layout(location = 1) out vec4 color_interp;
|
||||
layout(location = 2) out vec2 vertex_interp;
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
|
||||
layout(location = 3) out vec2 pixel_size_interp;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MATERIAL_UNIFORMS_USED
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
|
||||
|
||||
#MATERIAL_UNIFORMS
|
||||
|
||||
} material;
|
||||
#endif
|
||||
|
||||
#GLOBALS
|
||||
|
||||
#ifdef USE_ATTRIBUTES
|
||||
vec3 srgb_to_linear(vec3 color) {
|
||||
return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec4 instance_custom = vec4(0.0);
|
||||
#if defined(CUSTOM0_USED)
|
||||
vec4 custom0 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(CUSTOM1_USED)
|
||||
vec4 custom1 = vec4(0.0);
|
||||
#endif
|
||||
|
||||
#ifdef USE_PRIMITIVE
|
||||
|
||||
//weird bug,
|
||||
//this works
|
||||
vec2 vertex;
|
||||
vec2 uv;
|
||||
vec4 color;
|
||||
|
||||
if (gl_VertexIndex == 0) {
|
||||
vertex = draw_data.points[0];
|
||||
uv = draw_data.uvs[0];
|
||||
color = vec4(unpackHalf2x16(draw_data.colors[0]), unpackHalf2x16(draw_data.colors[1]));
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
vertex = draw_data.points[1];
|
||||
uv = draw_data.uvs[1];
|
||||
color = vec4(unpackHalf2x16(draw_data.colors[2]), unpackHalf2x16(draw_data.colors[3]));
|
||||
} else {
|
||||
vertex = draw_data.points[2];
|
||||
uv = draw_data.uvs[2];
|
||||
color = vec4(unpackHalf2x16(draw_data.colors[4]), unpackHalf2x16(draw_data.colors[5]));
|
||||
}
|
||||
uvec4 bones = uvec4(0, 0, 0, 0);
|
||||
vec4 bone_weights = vec4(0.0);
|
||||
|
||||
#elif defined(USE_ATTRIBUTES)
|
||||
|
||||
vec2 vertex = vertex_attrib;
|
||||
vec4 color = color_attrib;
|
||||
if (bool(draw_data.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR)) {
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
}
|
||||
color *= draw_data.modulation;
|
||||
vec2 uv = uv_attrib;
|
||||
|
||||
#if defined(CUSTOM0_USED)
|
||||
custom0 = custom0_attrib;
|
||||
#endif
|
||||
|
||||
#if defined(CUSTOM1_USED)
|
||||
custom1 = custom1_attrib;
|
||||
#endif
|
||||
|
||||
uvec4 bones = bone_attrib;
|
||||
vec4 bone_weights = weight_attrib;
|
||||
#else // !USE_ATTRIBUTES
|
||||
|
||||
vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
|
||||
vec2 vertex_base = vertex_base_arr[gl_VertexIndex];
|
||||
|
||||
vec2 uv = draw_data.src_rect.xy + abs(draw_data.src_rect.zw) * ((draw_data.flags & FLAGS_TRANSPOSE_RECT) != 0 ? vertex_base.yx : vertex_base.xy);
|
||||
vec4 color = draw_data.modulation;
|
||||
vec2 vertex = draw_data.dst_rect.xy + abs(draw_data.dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data.src_rect.zw, vec2(0.0, 0.0)));
|
||||
uvec4 bones = uvec4(0, 0, 0, 0);
|
||||
|
||||
#endif // USE_ATTRIBUTES
|
||||
|
||||
mat4 model_matrix = mat4(vec4(draw_data.world_x, 0.0, 0.0), vec4(draw_data.world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data.world_ofs, 0.0, 1.0));
|
||||
|
||||
#define FLAGS_INSTANCING_MASK 0x7F
|
||||
#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
|
||||
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
|
||||
|
||||
uint instancing = draw_data.flags & FLAGS_INSTANCING_MASK;
|
||||
|
||||
#ifdef USE_ATTRIBUTES
|
||||
if (instancing > 1) {
|
||||
// trails
|
||||
|
||||
uint stride = 2 + 1 + 1; //particles always uses this format
|
||||
|
||||
uint trail_size = instancing;
|
||||
|
||||
uint offset = trail_size * stride * gl_InstanceIndex;
|
||||
|
||||
vec4 pcolor;
|
||||
vec2 new_vertex;
|
||||
{
|
||||
uint boffset = offset + bone_attrib.x * stride;
|
||||
new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x;
|
||||
pcolor = transforms.data[boffset + 2] * weight_attrib.x;
|
||||
}
|
||||
if (weight_attrib.y > 0.001) {
|
||||
uint boffset = offset + bone_attrib.y * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.y;
|
||||
}
|
||||
if (weight_attrib.z > 0.001) {
|
||||
uint boffset = offset + bone_attrib.z * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.z;
|
||||
}
|
||||
if (weight_attrib.w > 0.001) {
|
||||
uint boffset = offset + bone_attrib.w * stride;
|
||||
new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w;
|
||||
pcolor += transforms.data[boffset + 2] * weight_attrib.w;
|
||||
}
|
||||
|
||||
instance_custom = transforms.data[offset + 3];
|
||||
|
||||
vertex = new_vertex;
|
||||
color *= pcolor;
|
||||
} else
|
||||
#endif // USE_ATTRIBUTES
|
||||
{
|
||||
if (instancing == 1) {
|
||||
uint stride = 2;
|
||||
{
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) {
|
||||
stride += 1;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
|
||||
stride += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint offset = stride * gl_InstanceIndex;
|
||||
|
||||
mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
|
||||
offset += 2;
|
||||
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_COLORS)) {
|
||||
color *= transforms.data[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (bool(draw_data.flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
|
||||
instance_custom = transforms.data[offset];
|
||||
}
|
||||
|
||||
matrix = transpose(matrix);
|
||||
model_matrix = model_matrix * matrix;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_POINT_SIZE
|
||||
float point_size = 1.0;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WORLD_VERTEX_COORDS
|
||||
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
|
||||
#endif
|
||||
{
|
||||
#CODE : VERTEX
|
||||
}
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base;
|
||||
#endif
|
||||
|
||||
#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
|
||||
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
|
||||
#endif
|
||||
|
||||
color_interp = color;
|
||||
|
||||
if (canvas_data.use_pixel_snap) {
|
||||
vertex = floor(vertex + 0.5);
|
||||
// precision issue on some hardware creates artifacts within texture
|
||||
// offset uv by a small amount to avoid
|
||||
uv += 1e-5;
|
||||
}
|
||||
|
||||
vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
|
||||
|
||||
vertex_interp = vertex;
|
||||
uv_interp = uv;
|
||||
|
||||
gl_Position = canvas_data.screen_transform * vec4(vertex, 0.0, 1.0);
|
||||
|
||||
#ifdef USE_POINT_SIZE
|
||||
gl_PointSize = point_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "canvas_uniforms_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(location = 1) in vec4 color_interp;
|
||||
layout(location = 2) in vec2 vertex_interp;
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
|
||||
layout(location = 3) in vec2 pixel_size_interp;
|
||||
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
#ifdef MATERIAL_UNIFORMS_USED
|
||||
layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
|
||||
|
||||
#MATERIAL_UNIFORMS
|
||||
|
||||
} material;
|
||||
#endif
|
||||
|
||||
vec2 screen_uv_to_sdf(vec2 p_uv) {
|
||||
return canvas_data.screen_to_sdf * p_uv;
|
||||
}
|
||||
|
||||
float texture_sdf(vec2 p_sdf) {
|
||||
vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw;
|
||||
float d = texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv).r;
|
||||
d *= SDF_MAX_LENGTH;
|
||||
return d * canvas_data.tex_to_sdf;
|
||||
}
|
||||
|
||||
vec2 texture_sdf_normal(vec2 p_sdf) {
|
||||
vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw;
|
||||
|
||||
const float EPSILON = 0.001;
|
||||
return normalize(vec2(
|
||||
texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv - vec2(EPSILON, 0.0)).r,
|
||||
texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv - vec2(0.0, EPSILON)).r));
|
||||
}
|
||||
|
||||
vec2 sdf_to_screen_uv(vec2 p_sdf) {
|
||||
return p_sdf * canvas_data.sdf_to_screen;
|
||||
}
|
||||
|
||||
#GLOBALS
|
||||
|
||||
#ifdef LIGHT_CODE_USED
|
||||
|
||||
vec4 light_compute(
|
||||
vec3 light_vertex,
|
||||
vec3 light_position,
|
||||
vec3 normal,
|
||||
vec4 light_color,
|
||||
float light_energy,
|
||||
vec4 specular_shininess,
|
||||
inout vec4 shadow_modulate,
|
||||
vec2 screen_uv,
|
||||
vec2 uv,
|
||||
vec4 color, bool is_directional) {
|
||||
vec4 light = vec4(0.0);
|
||||
vec3 light_direction = vec3(0.0);
|
||||
|
||||
if (is_directional) {
|
||||
light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z));
|
||||
light_position = vec3(0.0);
|
||||
} else {
|
||||
light_direction = normalize(light_position - light_vertex);
|
||||
}
|
||||
|
||||
#CODE : LIGHT
|
||||
|
||||
return light;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
|
||||
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
|
||||
float tex_size = 1.0 / tex_pixel_size;
|
||||
|
||||
if (pixel < margin_begin) {
|
||||
return pixel * tex_pixel_size;
|
||||
} else if (pixel >= draw_size - margin_end) {
|
||||
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
|
||||
} else {
|
||||
if (!bool(draw_data.flags & FLAGS_NINEPACH_DRAW_CENTER)) {
|
||||
draw_center--;
|
||||
}
|
||||
|
||||
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
|
||||
if (np_repeat == 0) { // Stretch.
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
|
||||
} else if (np_repeat == 1) { // Tile.
|
||||
// Convert to offset.
|
||||
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ofs) * tex_pixel_size;
|
||||
} else if (np_repeat == 2) { // Tile Fit.
|
||||
// Calculate scale.
|
||||
float src_area = draw_size - margin_begin - margin_end;
|
||||
float dst_area = tex_size - margin_begin - margin_end;
|
||||
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - margin_begin) / src_area;
|
||||
ratio = mod(ratio * scale, 1.0);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * dst_area) * tex_pixel_size;
|
||||
} else { // Shouldn't happen, but silences compiler warning.
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTING
|
||||
|
||||
vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
|
||||
float cNdotL = max(0.0, dot(normal, light_vec));
|
||||
|
||||
if (specular_shininess_used) {
|
||||
//blinn
|
||||
vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
|
||||
vec3 half_vec = normalize(view + light_vec);
|
||||
|
||||
float cNdotV = max(dot(normal, view), 0.0);
|
||||
float cNdotH = max(dot(normal, half_vec), 0.0);
|
||||
float cVdotH = max(dot(view, half_vec), 0.0);
|
||||
float cLdotH = max(dot(light_vec, half_vec), 0.0);
|
||||
float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
|
||||
float blinn = pow(cNdotH, shininess);
|
||||
blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
|
||||
float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
|
||||
|
||||
return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
|
||||
} else {
|
||||
return light_color * base_color * cNdotL;
|
||||
}
|
||||
}
|
||||
|
||||
//float distance = length(shadow_pos);
|
||||
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
|
||||
#ifdef LIGHT_CODE_USED
|
||||
,
|
||||
vec3 shadow_modulate
|
||||
#endif
|
||||
) {
|
||||
float shadow;
|
||||
uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
|
||||
|
||||
if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
|
||||
shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
|
||||
} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
|
||||
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
|
||||
shadow = 0.0;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
|
||||
shadow /= 5.0;
|
||||
} else { //PCF13
|
||||
vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
|
||||
shadow = 0.0;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
|
||||
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
|
||||
shadow /= 13.0;
|
||||
}
|
||||
|
||||
vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
|
||||
#ifdef LIGHT_CODE_USED
|
||||
shadow_color.rgb *= shadow_modulate;
|
||||
#endif
|
||||
|
||||
shadow_color.a *= light_color.a; //respect light alpha
|
||||
|
||||
return mix(light_color, shadow_color, shadow);
|
||||
}
|
||||
|
||||
void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
|
||||
uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
|
||||
|
||||
switch (blend_mode) {
|
||||
case LIGHT_FLAGS_BLEND_MODE_ADD: {
|
||||
color.rgb += light_color.rgb * light_color.a;
|
||||
} break;
|
||||
case LIGHT_FLAGS_BLEND_MODE_SUB: {
|
||||
color.rgb -= light_color.rgb * light_color.a;
|
||||
} break;
|
||||
case LIGHT_FLAGS_BLEND_MODE_MIX: {
|
||||
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
float msdf_median(float r, float g, float b, float a) {
|
||||
return min(max(min(r, g), min(max(r, g), b)), a);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = color_interp;
|
||||
vec2 uv = uv_interp;
|
||||
vec2 vertex = vertex_interp;
|
||||
|
||||
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
|
||||
|
||||
#ifdef USE_NINEPATCH
|
||||
|
||||
int draw_center = 2;
|
||||
uv = vec2(
|
||||
map_ninepatch_axis(pixel_size_interp.x, abs(draw_data.dst_rect.z), draw_data.color_texture_pixel_size.x, draw_data.ninepatch_margins.x, draw_data.ninepatch_margins.z, int(draw_data.flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
|
||||
map_ninepatch_axis(pixel_size_interp.y, abs(draw_data.dst_rect.w), draw_data.color_texture_pixel_size.y, draw_data.ninepatch_margins.y, draw_data.ninepatch_margins.w, int(draw_data.flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
|
||||
|
||||
if (draw_center == 0) {
|
||||
color.a = 0.0;
|
||||
}
|
||||
|
||||
uv = uv * draw_data.src_rect.zw + draw_data.src_rect.xy; //apply region if needed
|
||||
|
||||
#endif
|
||||
if (bool(draw_data.flags & FLAGS_CLIP_RECT_UV)) {
|
||||
uv = clamp(uv, draw_data.src_rect.xy, draw_data.src_rect.xy + abs(draw_data.src_rect.zw));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef USE_PRIMITIVE
|
||||
if (bool(draw_data.flags & FLAGS_USE_MSDF)) {
|
||||
float px_range = draw_data.ninepatch_margins.x;
|
||||
float outline_thickness = draw_data.ninepatch_margins.y;
|
||||
//float reserved1 = draw_data.ninepatch_margins.z;
|
||||
//float reserved2 = draw_data.ninepatch_margins.w;
|
||||
|
||||
vec4 msdf_sample = texture(sampler2D(color_texture, texture_sampler), uv);
|
||||
vec2 msdf_size = vec2(textureSize(sampler2D(color_texture, texture_sampler), 0));
|
||||
vec2 dest_size = vec2(1.0) / fwidth(uv);
|
||||
float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
|
||||
float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5;
|
||||
|
||||
if (outline_thickness > 0) {
|
||||
float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range;
|
||||
float a = clamp((d + cr) * px_size, 0.0, 1.0);
|
||||
color.a = a * color.a;
|
||||
} else {
|
||||
float a = clamp(d * px_size + 0.5, 0.0, 1.0);
|
||||
color.a = a * color.a;
|
||||
}
|
||||
} else if (bool(draw_data.flags & FLAGS_USE_LCD)) {
|
||||
vec4 lcd_sample = texture(sampler2D(color_texture, texture_sampler), uv);
|
||||
if (lcd_sample.a == 1.0) {
|
||||
color.rgb = lcd_sample.rgb * color.a;
|
||||
} else {
|
||||
color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
color *= texture(sampler2D(color_texture, texture_sampler), uv);
|
||||
}
|
||||
|
||||
uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
|
||||
bool using_light = light_count > 0 || canvas_data.directional_light_count > 0;
|
||||
|
||||
vec3 normal;
|
||||
|
||||
#if defined(NORMAL_USED)
|
||||
bool normal_used = true;
|
||||
#else
|
||||
bool normal_used = false;
|
||||
#endif
|
||||
|
||||
if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
|
||||
normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
|
||||
if (bool(draw_data.flags & FLAGS_TRANSPOSE_RECT)) {
|
||||
normal.xy = normal.yx;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_FLIP_H)) {
|
||||
normal.x = -normal.x;
|
||||
}
|
||||
if (bool(draw_data.flags & FLAGS_FLIP_V)) {
|
||||
normal.y = -normal.y;
|
||||
}
|
||||
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
|
||||
normal_used = true;
|
||||
} else {
|
||||
normal = vec3(0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec4 specular_shininess;
|
||||
|
||||
#if defined(SPECULAR_SHININESS_USED)
|
||||
|
||||
bool specular_shininess_used = true;
|
||||
#else
|
||||
bool specular_shininess_used = false;
|
||||
#endif
|
||||
|
||||
if (specular_shininess_used || (using_light && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
|
||||
specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
|
||||
specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
|
||||
specular_shininess_used = true;
|
||||
} else {
|
||||
specular_shininess = vec4(1.0);
|
||||
}
|
||||
|
||||
#if defined(SCREEN_UV_USED)
|
||||
vec2 screen_uv = gl_FragCoord.xy * canvas_data.screen_pixel_size;
|
||||
#else
|
||||
vec2 screen_uv = vec2(0.0);
|
||||
#endif
|
||||
|
||||
vec3 light_vertex = vec3(vertex, 0.0);
|
||||
vec2 shadow_vertex = vertex;
|
||||
|
||||
{
|
||||
float normal_map_depth = 1.0;
|
||||
|
||||
#if defined(NORMAL_MAP_USED)
|
||||
vec3 normal_map = vec3(0.0, 0.0, 1.0);
|
||||
normal_used = true;
|
||||
#endif
|
||||
|
||||
#CODE : FRAGMENT
|
||||
|
||||
#if defined(NORMAL_MAP_USED)
|
||||
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (normal_used) {
|
||||
//convert by item transform
|
||||
normal.xy = mat2(normalize(draw_data.world_x), normalize(draw_data.world_y)) * normal.xy;
|
||||
//convert by canvas transform
|
||||
normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
|
||||
}
|
||||
|
||||
vec4 base_color = color;
|
||||
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
float light_only_alpha = 0.0;
|
||||
#elif !defined(MODE_UNSHADED)
|
||||
color *= canvas_data.canvas_modulation;
|
||||
#endif
|
||||
|
||||
#if defined(USE_LIGHTING) && !defined(MODE_UNSHADED)
|
||||
|
||||
// Directional Lights
|
||||
|
||||
for (uint i = 0; i < canvas_data.directional_light_count; i++) {
|
||||
uint light_base = i;
|
||||
|
||||
vec2 direction = light_array.data[light_base].position;
|
||||
vec4 light_color = light_array.data[light_base].color;
|
||||
|
||||
#ifdef LIGHT_CODE_USED
|
||||
|
||||
vec4 shadow_modulate = vec4(1.0);
|
||||
light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true);
|
||||
#else
|
||||
|
||||
if (normal_used) {
|
||||
vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
|
||||
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
|
||||
} else {
|
||||
light_color.rgb *= base_color.rgb;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
|
||||
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
|
||||
|
||||
vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
|
||||
|
||||
light_color = light_shadow_compute(light_base, light_color, shadow_uv
|
||||
#ifdef LIGHT_CODE_USED
|
||||
,
|
||||
shadow_modulate.rgb
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
light_blend_compute(light_base, light_color, color.rgb);
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
light_only_alpha += light_color.a;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Positional Lights
|
||||
|
||||
for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
|
||||
if (i >= light_count) {
|
||||
break;
|
||||
}
|
||||
uint light_base = draw_data.lights[i >> 2];
|
||||
light_base >>= (i & 3) * 8;
|
||||
light_base &= 0xFF;
|
||||
|
||||
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
|
||||
vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy;
|
||||
|
||||
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
|
||||
//if outside the light texture, light color is zero
|
||||
continue;
|
||||
}
|
||||
|
||||
vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0);
|
||||
vec4 light_base_color = light_array.data[light_base].color;
|
||||
|
||||
#ifdef LIGHT_CODE_USED
|
||||
|
||||
vec4 shadow_modulate = vec4(1.0);
|
||||
vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
|
||||
|
||||
light_color.rgb *= light_base_color.rgb;
|
||||
light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false);
|
||||
#else
|
||||
|
||||
light_color.rgb *= light_base_color.rgb * light_base_color.a;
|
||||
|
||||
if (normal_used) {
|
||||
vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
|
||||
vec3 pos = light_vertex;
|
||||
vec3 light_vec = normalize(light_pos - pos);
|
||||
|
||||
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
|
||||
} else {
|
||||
light_color.rgb *= base_color.rgb;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
|
||||
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
|
||||
|
||||
vec2 pos_norm = normalize(shadow_pos);
|
||||
vec2 pos_abs = abs(pos_norm);
|
||||
vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
|
||||
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
|
||||
float tex_ofs;
|
||||
float distance;
|
||||
if (pos_rot.y > 0) {
|
||||
if (pos_rot.x > 0) {
|
||||
tex_ofs = pos_box.y * 0.125 + 0.125;
|
||||
distance = shadow_pos.x;
|
||||
} else {
|
||||
tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
|
||||
distance = shadow_pos.y;
|
||||
}
|
||||
} else {
|
||||
if (pos_rot.x < 0) {
|
||||
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
|
||||
distance = -shadow_pos.x;
|
||||
} else {
|
||||
tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
|
||||
distance = -shadow_pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
distance *= light_array.data[light_base].shadow_zfar_inv;
|
||||
|
||||
//float distance = length(shadow_pos);
|
||||
vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
|
||||
|
||||
light_color = light_shadow_compute(light_base, light_color, shadow_uv
|
||||
#ifdef LIGHT_CODE_USED
|
||||
,
|
||||
shadow_modulate.rgb
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
light_blend_compute(light_base, light_color, color.rgb);
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
light_only_alpha += light_color.a;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_LIGHT_ONLY
|
||||
color.a *= light_only_alpha;
|
||||
#endif
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in highp vec3 vertex;
|
||||
|
||||
layout(push_constant, std430) uniform Constants {
|
||||
mat4 projection;
|
||||
mat2x4 modelview;
|
||||
vec2 direction;
|
||||
float z_far;
|
||||
float pad;
|
||||
}
|
||||
constants;
|
||||
|
||||
#ifdef MODE_SHADOW
|
||||
layout(location = 0) out highp float depth;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
highp vec4 vtx = vec4(vertex, 1.0) * mat4(constants.modelview[0], constants.modelview[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
#ifdef MODE_SHADOW
|
||||
depth = dot(constants.direction, vtx.xy);
|
||||
#endif
|
||||
gl_Position = constants.projection * vtx;
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Constants {
|
||||
mat4 projection;
|
||||
mat2x4 modelview;
|
||||
vec2 direction;
|
||||
float z_far;
|
||||
float pad;
|
||||
}
|
||||
constants;
|
||||
|
||||
#ifdef MODE_SHADOW
|
||||
layout(location = 0) in highp float depth;
|
||||
layout(location = 0) out highp float distance_buf;
|
||||
#else
|
||||
layout(location = 0) out highp float sdf_buf;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_SHADOW
|
||||
distance_buf = depth / constants.z_far;
|
||||
#else
|
||||
sdf_buf = 1.0;
|
||||
#endif
|
||||
}
|
||||
179
engine/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
Normal file
179
engine/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(r8, set = 0, binding = 1) uniform restrict readonly image2D src_pixels;
|
||||
layout(r16_snorm, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf;
|
||||
|
||||
layout(rg16i, set = 0, binding = 3) uniform restrict readonly iimage2D src_process;
|
||||
layout(rg16i, set = 0, binding = 4) uniform restrict writeonly iimage2D dst_process;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
ivec2 size;
|
||||
int stride;
|
||||
int shift;
|
||||
ivec2 base_size;
|
||||
uvec2 pad;
|
||||
}
|
||||
params;
|
||||
|
||||
#define SDF_MAX_LENGTH 16384.0
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(pos, params.size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODE_LOAD
|
||||
|
||||
bool solid = imageLoad(src_pixels, pos).r > 0.5;
|
||||
imageStore(dst_process, pos, solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0));
|
||||
#endif
|
||||
|
||||
#ifdef MODE_LOAD_SHRINK
|
||||
|
||||
int s = 1 << params.shift;
|
||||
ivec2 base = pos << params.shift;
|
||||
ivec2 center = base + ivec2(params.shift);
|
||||
|
||||
ivec2 rel = ivec2(32767);
|
||||
float d = 1e20;
|
||||
int found = 0;
|
||||
int solid_found = 0;
|
||||
for (int i = 0; i < s; i++) {
|
||||
for (int j = 0; j < s; j++) {
|
||||
ivec2 src_pos = base + ivec2(i, j);
|
||||
if (any(greaterThanEqual(src_pos, params.base_size))) {
|
||||
continue;
|
||||
}
|
||||
bool solid = imageLoad(src_pixels, src_pos).r > 0.5;
|
||||
if (solid) {
|
||||
float dist = length(vec2(src_pos - center));
|
||||
if (dist < d) {
|
||||
d = dist;
|
||||
rel = src_pos;
|
||||
}
|
||||
solid_found++;
|
||||
}
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
if (solid_found == found) {
|
||||
//mark solid only if all are solid
|
||||
rel = ivec2(-32767);
|
||||
}
|
||||
|
||||
imageStore(dst_process, pos, ivec4(rel, 0, 0));
|
||||
#endif
|
||||
|
||||
#ifdef MODE_PROCESS
|
||||
|
||||
ivec2 base = pos << params.shift;
|
||||
ivec2 center = base + ivec2(params.shift);
|
||||
|
||||
ivec2 rel = imageLoad(src_process, pos).xy;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
if (center != rel) {
|
||||
//only process if it does not point to itself
|
||||
const int ofs_table_size = 8;
|
||||
const ivec2 ofs_table[ofs_table_size] = ivec2[](
|
||||
ivec2(-1, -1),
|
||||
ivec2(0, -1),
|
||||
ivec2(+1, -1),
|
||||
|
||||
ivec2(-1, 0),
|
||||
ivec2(+1, 0),
|
||||
|
||||
ivec2(-1, +1),
|
||||
ivec2(0, +1),
|
||||
ivec2(+1, +1));
|
||||
|
||||
float dist = length(vec2(rel - center));
|
||||
for (int i = 0; i < ofs_table_size; i++) {
|
||||
ivec2 src_pos = pos + ofs_table[i] * params.stride;
|
||||
if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, params.size))) {
|
||||
continue;
|
||||
}
|
||||
ivec2 src_rel = imageLoad(src_process, src_pos).xy;
|
||||
bool src_solid = src_rel.x < 0;
|
||||
if (src_solid) {
|
||||
src_rel = -src_rel - ivec2(1);
|
||||
}
|
||||
|
||||
if (src_solid != solid) {
|
||||
src_rel = ivec2(src_pos << params.shift); //point to itself if of different type
|
||||
}
|
||||
|
||||
float src_dist = length(vec2(src_rel - center));
|
||||
if (src_dist < dist) {
|
||||
dist = src_dist;
|
||||
rel = src_rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
imageStore(dst_process, pos, ivec4(rel, 0, 0));
|
||||
#endif
|
||||
|
||||
#ifdef MODE_STORE
|
||||
|
||||
ivec2 rel = imageLoad(src_process, pos).xy;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
float d = length(vec2(rel - pos));
|
||||
|
||||
if (solid) {
|
||||
d = -d;
|
||||
}
|
||||
|
||||
d /= SDF_MAX_LENGTH;
|
||||
d = clamp(d, -1.0, 1.0);
|
||||
imageStore(dst_sdf, pos, vec4(d));
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_STORE_SHRINK
|
||||
|
||||
ivec2 base = pos << params.shift;
|
||||
ivec2 center = base + ivec2(params.shift);
|
||||
|
||||
ivec2 rel = imageLoad(src_process, pos).xy;
|
||||
|
||||
bool solid = rel.x < 0;
|
||||
|
||||
if (solid) {
|
||||
rel = -rel - ivec2(1);
|
||||
}
|
||||
|
||||
float d = length(vec2(rel - center));
|
||||
|
||||
if (solid) {
|
||||
d = -d;
|
||||
}
|
||||
d /= SDF_MAX_LENGTH;
|
||||
d = clamp(d, -1.0, 1.0);
|
||||
imageStore(dst_sdf, pos, vec4(d));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
|
||||
#define MAX_LIGHTS_PER_ITEM 16
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
#define SDF_MAX_LENGTH 16384.0
|
||||
|
||||
//1 means enabled, 2+ means trails in use
|
||||
#define FLAGS_INSTANCING_MASK 0x7F
|
||||
#define FLAGS_INSTANCING_HAS_COLORS (1 << 7)
|
||||
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA (1 << 8)
|
||||
|
||||
#define FLAGS_CLIP_RECT_UV (1 << 9)
|
||||
#define FLAGS_TRANSPOSE_RECT (1 << 10)
|
||||
#define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11)
|
||||
#define FLAGS_NINEPACH_DRAW_CENTER (1 << 12)
|
||||
|
||||
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
|
||||
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
|
||||
|
||||
#define FLAGS_LIGHT_COUNT_SHIFT 20
|
||||
|
||||
#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
|
||||
#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
|
||||
|
||||
#define FLAGS_USE_MSDF (1 << 28)
|
||||
#define FLAGS_USE_LCD (1 << 29)
|
||||
|
||||
#define FLAGS_FLIP_H (1 << 30)
|
||||
#define FLAGS_FLIP_V (1 << 31)
|
||||
|
||||
// Push Constant
|
||||
|
||||
layout(push_constant, std430) uniform DrawData {
|
||||
vec2 world_x;
|
||||
vec2 world_y;
|
||||
vec2 world_ofs;
|
||||
uint flags;
|
||||
uint specular_shininess;
|
||||
#ifdef USE_PRIMITIVE
|
||||
vec2 points[3];
|
||||
vec2 uvs[3];
|
||||
uint colors[6];
|
||||
#else
|
||||
vec4 modulation;
|
||||
vec4 ninepatch_margins;
|
||||
vec4 dst_rect; //for built-in rect and UV
|
||||
vec4 src_rect;
|
||||
vec2 pad;
|
||||
|
||||
#endif
|
||||
vec2 color_texture_pixel_size;
|
||||
uint lights[4];
|
||||
}
|
||||
draw_data;
|
||||
|
||||
// In vulkan, sets should always be ordered using the following logic:
|
||||
// Lower Sets: Sets that change format and layout less often
|
||||
// Higher sets: Sets that change format and layout very often
|
||||
// This is because changing a set for another with a different layout or format,
|
||||
// invalidates all the upper ones (as likely internal base offset changes)
|
||||
|
||||
/* SET0: Globals */
|
||||
|
||||
// The values passed per draw primitives are cached within it
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform CanvasData {
|
||||
mat4 canvas_transform;
|
||||
mat4 screen_transform;
|
||||
mat4 canvas_normal_transform;
|
||||
vec4 canvas_modulation;
|
||||
vec2 screen_pixel_size;
|
||||
float time;
|
||||
bool use_pixel_snap;
|
||||
|
||||
vec4 sdf_to_tex;
|
||||
vec2 screen_to_sdf;
|
||||
vec2 sdf_to_screen;
|
||||
|
||||
uint directional_light_count;
|
||||
float tex_to_sdf;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
canvas_data;
|
||||
|
||||
#define LIGHT_FLAGS_BLEND_MASK (3 << 16)
|
||||
#define LIGHT_FLAGS_BLEND_MODE_ADD (0 << 16)
|
||||
#define LIGHT_FLAGS_BLEND_MODE_SUB (1 << 16)
|
||||
#define LIGHT_FLAGS_BLEND_MODE_MIX (2 << 16)
|
||||
#define LIGHT_FLAGS_BLEND_MODE_MASK (3 << 16)
|
||||
#define LIGHT_FLAGS_HAS_SHADOW (1 << 20)
|
||||
#define LIGHT_FLAGS_FILTER_SHIFT 22
|
||||
#define LIGHT_FLAGS_FILTER_MASK (3 << 22)
|
||||
#define LIGHT_FLAGS_SHADOW_NEAREST (0 << 22)
|
||||
#define LIGHT_FLAGS_SHADOW_PCF5 (1 << 22)
|
||||
#define LIGHT_FLAGS_SHADOW_PCF13 (2 << 22)
|
||||
|
||||
struct Light {
|
||||
mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
|
||||
mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
|
||||
vec4 color;
|
||||
|
||||
uint shadow_color; // packed
|
||||
uint flags; //index to light texture
|
||||
float shadow_pixel_size;
|
||||
float height;
|
||||
|
||||
vec2 position;
|
||||
float shadow_zfar_inv;
|
||||
float shadow_y_ofs;
|
||||
|
||||
vec4 atlas_rect;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 2, std140) uniform LightData {
|
||||
Light data[MAX_LIGHTS];
|
||||
}
|
||||
light_array;
|
||||
|
||||
layout(set = 0, binding = 3) uniform texture2D atlas_texture;
|
||||
layout(set = 0, binding = 4) uniform texture2D shadow_atlas_texture;
|
||||
|
||||
layout(set = 0, binding = 5) uniform sampler shadow_sampler;
|
||||
|
||||
layout(set = 0, binding = 6) uniform texture2D color_buffer;
|
||||
layout(set = 0, binding = 7) uniform texture2D sdf_texture;
|
||||
|
||||
#include "samplers_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalShaderUniformData {
|
||||
vec4 data[];
|
||||
}
|
||||
global_shader_uniforms;
|
||||
|
||||
/* SET1: Is reserved for the material */
|
||||
|
||||
//
|
||||
|
||||
/* SET2: Instancing and Skeleton */
|
||||
|
||||
layout(set = 2, binding = 0, std430) restrict readonly buffer Transforms {
|
||||
vec4 data[];
|
||||
}
|
||||
transforms;
|
||||
|
||||
/* SET3: Texture */
|
||||
|
||||
layout(set = 3, binding = 0) uniform texture2D color_texture;
|
||||
layout(set = 3, binding = 1) uniform texture2D normal_texture;
|
||||
layout(set = 3, binding = 2) uniform texture2D specular_texture;
|
||||
layout(set = 3, binding = 3) uniform sampler texture_sampler;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#define CLUSTER_COUNTER_SHIFT 20
|
||||
#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1)
|
||||
#define CLUSTER_COUNTER_MASK 0xfff
|
||||
115
engine/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
Normal file
115
engine/servers/rendering/renderer_rd/shaders/cluster_debug.glsl
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
const vec3 usage_gradient[33] = vec3[]( // 1 (none) + 32
|
||||
vec3(0.14, 0.17, 0.23),
|
||||
vec3(0.24, 0.44, 0.83),
|
||||
vec3(0.23, 0.57, 0.84),
|
||||
vec3(0.22, 0.71, 0.84),
|
||||
vec3(0.22, 0.85, 0.83),
|
||||
vec3(0.21, 0.85, 0.72),
|
||||
vec3(0.21, 0.85, 0.57),
|
||||
vec3(0.20, 0.85, 0.42),
|
||||
vec3(0.20, 0.85, 0.27),
|
||||
vec3(0.27, 0.86, 0.19),
|
||||
vec3(0.51, 0.85, 0.19),
|
||||
vec3(0.57, 0.86, 0.19),
|
||||
vec3(0.62, 0.85, 0.19),
|
||||
vec3(0.67, 0.86, 0.20),
|
||||
vec3(0.73, 0.85, 0.20),
|
||||
vec3(0.78, 0.85, 0.20),
|
||||
vec3(0.83, 0.85, 0.20),
|
||||
vec3(0.85, 0.82, 0.20),
|
||||
vec3(0.85, 0.76, 0.20),
|
||||
vec3(0.85, 0.81, 0.20),
|
||||
vec3(0.85, 0.65, 0.20),
|
||||
vec3(0.84, 0.60, 0.21),
|
||||
vec3(0.84, 0.56, 0.21),
|
||||
vec3(0.84, 0.51, 0.21),
|
||||
vec3(0.84, 0.46, 0.21),
|
||||
vec3(0.84, 0.41, 0.21),
|
||||
vec3(0.84, 0.36, 0.21),
|
||||
vec3(0.84, 0.31, 0.21),
|
||||
vec3(0.84, 0.27, 0.21),
|
||||
vec3(0.83, 0.22, 0.22),
|
||||
vec3(0.83, 0.22, 0.27),
|
||||
vec3(0.83, 0.22, 0.32),
|
||||
vec3(1.00, 0.63, 0.70));
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uvec2 screen_size;
|
||||
uvec2 cluster_screen_size;
|
||||
|
||||
uint cluster_shift;
|
||||
uint cluster_type;
|
||||
float z_near;
|
||||
float z_far;
|
||||
|
||||
bool orthogonal;
|
||||
uint max_cluster_element_count_div_32;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std430) buffer restrict readonly ClusterData {
|
||||
uint data[];
|
||||
}
|
||||
cluster_data;
|
||||
|
||||
layout(rgba16f, set = 0, binding = 2) uniform restrict writeonly image2D screen_buffer;
|
||||
layout(set = 0, binding = 3) uniform texture2D depth_buffer;
|
||||
layout(set = 0, binding = 4) uniform sampler depth_buffer_sampler;
|
||||
|
||||
void main() {
|
||||
uvec2 screen_pos = gl_GlobalInvocationID.xy;
|
||||
if (any(greaterThanEqual(screen_pos, params.screen_size))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uvec2 cluster_pos = screen_pos >> params.cluster_shift;
|
||||
|
||||
uint offset = cluster_pos.y * params.cluster_screen_size.x + cluster_pos.x;
|
||||
offset += params.cluster_screen_size.x * params.cluster_screen_size.y * params.cluster_type;
|
||||
offset *= (params.max_cluster_element_count_div_32 + 32);
|
||||
|
||||
//depth buffers generally can't be accessed via image API
|
||||
float depth = texelFetch(sampler2D(depth_buffer, depth_buffer_sampler), ivec2(screen_pos), 0).r * 2.0 - 1.0;
|
||||
|
||||
if (params.orthogonal) {
|
||||
depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
|
||||
} else {
|
||||
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
|
||||
}
|
||||
depth /= params.z_far;
|
||||
|
||||
uint slice = uint(clamp(floor(depth * 32.0), 0.0, 31.0));
|
||||
uint slice_minmax = cluster_data.data[offset + params.max_cluster_element_count_div_32 + slice];
|
||||
uint item_min = slice_minmax & 0xFFFF;
|
||||
uint item_max = slice_minmax >> 16;
|
||||
|
||||
uint item_count = 0;
|
||||
for (uint i = 0; i < params.max_cluster_element_count_div_32; i++) {
|
||||
uint slice_bits = cluster_data.data[offset + i];
|
||||
while (slice_bits != 0) {
|
||||
uint bit = findLSB(slice_bits);
|
||||
uint item = i * 32 + bit;
|
||||
if ((item >= item_min && item < item_max)) {
|
||||
item_count++;
|
||||
}
|
||||
slice_bits &= ~(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
item_count = min(item_count, 32);
|
||||
|
||||
vec3 color = usage_gradient[item_count];
|
||||
|
||||
color = mix(color * 1.2, color * 0.3, float(slice) / 31.0);
|
||||
|
||||
imageStore(screen_buffer, ivec2(screen_pos), vec4(color, 1.0));
|
||||
}
|
||||
187
engine/servers/rendering/renderer_rd/shaders/cluster_render.glsl
Normal file
187
engine/servers/rendering/renderer_rd/shaders/cluster_render.glsl
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in vec3 vertex_attrib;
|
||||
|
||||
layout(location = 0) out float depth_interp;
|
||||
layout(location = 1) out flat uint element_index;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint base_index;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform State {
|
||||
mat4 projection;
|
||||
|
||||
float inv_z_far;
|
||||
uint screen_to_clusters_shift; // shift to obtain coordinates in block indices
|
||||
uint cluster_screen_width; //
|
||||
uint cluster_data_size; // how much data for a single cluster takes
|
||||
|
||||
uint cluster_depth_offset;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
state;
|
||||
|
||||
struct RenderElement {
|
||||
uint type; //0-4
|
||||
bool touches_near;
|
||||
bool touches_far;
|
||||
uint original_index;
|
||||
mat3x4 transform_inv;
|
||||
vec3 scale;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 2, std430) buffer restrict readonly RenderElements {
|
||||
RenderElement data[];
|
||||
}
|
||||
render_elements;
|
||||
|
||||
void main() {
|
||||
element_index = params.base_index + gl_InstanceIndex;
|
||||
|
||||
vec3 vertex = vertex_attrib;
|
||||
vertex *= render_elements.data[element_index].scale;
|
||||
|
||||
vertex = vec4(vertex, 1.0) * render_elements.data[element_index].transform_inv;
|
||||
depth_interp = -vertex.z;
|
||||
|
||||
gl_Position = state.projection * vec4(vertex, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
#ifndef MOLTENVK_USED // Metal will corrupt GPU state otherwise
|
||||
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) && defined(has_GL_KHR_shader_subgroup_vote)
|
||||
|
||||
#extension GL_KHR_shader_subgroup_ballot : enable
|
||||
#extension GL_KHR_shader_subgroup_arithmetic : enable
|
||||
#extension GL_KHR_shader_subgroup_vote : enable
|
||||
|
||||
#define USE_SUBGROUPS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
layout(location = 0) in float depth_interp;
|
||||
layout(location = 1) in flat uint element_index;
|
||||
|
||||
layout(set = 0, binding = 1, std140) uniform State {
|
||||
mat4 projection;
|
||||
float inv_z_far;
|
||||
uint screen_to_clusters_shift; // shift to obtain coordinates in block indices
|
||||
uint cluster_screen_width; //
|
||||
uint cluster_data_size; // how much data for a single cluster takes
|
||||
uint cluster_depth_offset;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
state;
|
||||
|
||||
//cluster data is layout linearly, each cell contains the follow information:
|
||||
// - list of bits for every element to mark as used, so (max_elem_count/32)*4 uints
|
||||
// - a uint for each element to mark the depth bits used when rendering (0-31)
|
||||
|
||||
layout(set = 0, binding = 3, std430) buffer restrict ClusterRender {
|
||||
uint data[];
|
||||
}
|
||||
cluster_render;
|
||||
|
||||
#ifdef USE_ATTACHMENT
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
//convert from screen to cluster
|
||||
uvec2 cluster = uvec2(gl_FragCoord.xy) >> state.screen_to_clusters_shift;
|
||||
|
||||
//get linear cluster offset from screen poss
|
||||
uint cluster_offset = cluster.x + state.cluster_screen_width * cluster.y;
|
||||
//multiply by data size to position at the beginning of the element list for this cluster
|
||||
cluster_offset *= state.cluster_data_size;
|
||||
|
||||
//find the current element in the list and plot the bit to mark it as used
|
||||
uint usage_write_offset = cluster_offset + (element_index >> 5);
|
||||
uint usage_write_bit = 1 << (element_index & 0x1F);
|
||||
|
||||
uint aux = 0;
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
|
||||
uint cluster_thread_group_index;
|
||||
|
||||
if (!gl_HelperInvocation) {
|
||||
//https://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
|
||||
|
||||
uvec4 mask;
|
||||
|
||||
while (true) {
|
||||
// find the cluster offset of the first active thread
|
||||
// threads that did break; go inactive and no longer count
|
||||
uint first = subgroupBroadcastFirst(cluster_offset);
|
||||
// update the mask for thread that match this cluster
|
||||
mask = subgroupBallot(first == cluster_offset);
|
||||
if (first == cluster_offset) {
|
||||
// This thread belongs to the group of threads that match this offset,
|
||||
// so exit the loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cluster_thread_group_index = subgroupBallotExclusiveBitCount(mask);
|
||||
|
||||
if (cluster_thread_group_index == 0) {
|
||||
aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// MoltenVK/Metal fails to compile shaders using gl_HelperInvocation for some GPUs
|
||||
#ifndef MOLTENVK_USED
|
||||
if (!gl_HelperInvocation)
|
||||
#endif
|
||||
{
|
||||
aux = atomicOr(cluster_render.data[usage_write_offset], usage_write_bit);
|
||||
}
|
||||
#endif
|
||||
//find the current element in the depth usage list and mark the current depth as used
|
||||
float unit_depth = depth_interp * state.inv_z_far;
|
||||
|
||||
uint z_bit = clamp(uint(floor(unit_depth * 32.0)), 0, 31);
|
||||
|
||||
uint z_write_offset = cluster_offset + state.cluster_depth_offset + element_index;
|
||||
uint z_write_bit = 1 << z_bit;
|
||||
|
||||
#ifdef USE_SUBGROUPS
|
||||
if (!gl_HelperInvocation) {
|
||||
z_write_bit = subgroupOr(z_write_bit); //merge all Zs
|
||||
if (cluster_thread_group_index == 0) {
|
||||
aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// MoltenVK/Metal fails to compile shaders using gl_HelperInvocation for some GPUs
|
||||
#ifndef MOLTENVK_USED
|
||||
if (!gl_HelperInvocation)
|
||||
#endif
|
||||
{
|
||||
aux = atomicOr(cluster_render.data[z_write_offset], z_write_bit);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTACHMENT
|
||||
frag_color = vec4(float(aux));
|
||||
#endif
|
||||
}
|
||||
119
engine/servers/rendering/renderer_rd/shaders/cluster_store.glsl
Normal file
119
engine/servers/rendering/renderer_rd/shaders/cluster_store.glsl
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint cluster_render_data_size; // how much data for a single cluster takes
|
||||
uint max_render_element_count_div_32; //divided by 32
|
||||
uvec2 cluster_screen_size;
|
||||
uint render_element_count_div_32; //divided by 32
|
||||
|
||||
uint max_cluster_element_count_div_32; //divided by 32
|
||||
uint pad1;
|
||||
uint pad2;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 1, std430) buffer restrict readonly ClusterRender {
|
||||
uint data[];
|
||||
}
|
||||
cluster_render;
|
||||
|
||||
layout(set = 0, binding = 2, std430) buffer restrict ClusterStore {
|
||||
uint data[];
|
||||
}
|
||||
cluster_store;
|
||||
|
||||
struct RenderElement {
|
||||
uint type; //0-4
|
||||
bool touches_near;
|
||||
bool touches_far;
|
||||
uint original_index;
|
||||
mat3x4 transform_inv;
|
||||
vec3 scale;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 3, std430) buffer restrict readonly RenderElements {
|
||||
RenderElement data[];
|
||||
}
|
||||
render_elements;
|
||||
|
||||
void main() {
|
||||
uvec2 pos = gl_GlobalInvocationID.xy;
|
||||
if (any(greaterThanEqual(pos, params.cluster_screen_size))) {
|
||||
return;
|
||||
}
|
||||
|
||||
//counter for each type of render_element
|
||||
|
||||
//base offset for this cluster
|
||||
uint base_offset = (pos.x + params.cluster_screen_size.x * pos.y);
|
||||
uint src_offset = base_offset * params.cluster_render_data_size;
|
||||
|
||||
uint render_element_offset = 0;
|
||||
|
||||
//check all render_elements and see which one was written to
|
||||
while (render_element_offset < params.render_element_count_div_32) {
|
||||
uint bits = cluster_render.data[src_offset + render_element_offset];
|
||||
while (bits != 0) {
|
||||
//if bits exist, check the render_element
|
||||
uint index_bit = findLSB(bits);
|
||||
uint index = render_element_offset * 32 + index_bit;
|
||||
uint type = render_elements.data[index].type;
|
||||
|
||||
uint z_range_offset = src_offset + params.max_render_element_count_div_32 + index;
|
||||
uint z_range = cluster_render.data[z_range_offset];
|
||||
|
||||
//if object was written, z was written, but check just in case
|
||||
if (z_range != 0) { //should always be > 0
|
||||
|
||||
uint from_z = findLSB(z_range);
|
||||
uint to_z = findMSB(z_range) + 1;
|
||||
|
||||
if (render_elements.data[index].touches_near) {
|
||||
from_z = 0;
|
||||
}
|
||||
|
||||
if (render_elements.data[index].touches_far) {
|
||||
to_z = 32;
|
||||
}
|
||||
|
||||
// find cluster offset in the buffer used for indexing in the renderer
|
||||
uint dst_offset = (base_offset + type * (params.cluster_screen_size.x * params.cluster_screen_size.y)) * (params.max_cluster_element_count_div_32 + 32);
|
||||
|
||||
uint orig_index = render_elements.data[index].original_index;
|
||||
//store this index in the Z slices by setting the relevant bit
|
||||
for (uint i = from_z; i < to_z; i++) {
|
||||
uint slice_ofs = dst_offset + params.max_cluster_element_count_div_32 + i;
|
||||
|
||||
uint minmax = cluster_store.data[slice_ofs];
|
||||
|
||||
if (minmax == 0) {
|
||||
minmax = 0xFFFF; //min 0, max 0xFFFF
|
||||
}
|
||||
|
||||
uint elem_min = min(orig_index, minmax & 0xFFFF);
|
||||
uint elem_max = max(orig_index + 1, minmax >> 16); //always store plus one, so zero means range is empty when not written to
|
||||
|
||||
minmax = elem_min | (elem_max << 16);
|
||||
cluster_store.data[slice_ofs] = minmax;
|
||||
}
|
||||
|
||||
uint store_word = orig_index >> 5;
|
||||
uint store_bit = orig_index & 0x1F;
|
||||
|
||||
//store the actual render_element index at the end, so the rendering code can reference it
|
||||
cluster_store.data[dst_offset + store_word] |= 1 << store_bit;
|
||||
}
|
||||
|
||||
bits &= ~(1 << index_bit); //clear the bit to continue iterating
|
||||
}
|
||||
|
||||
render_element_offset++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
struct DecalData {
|
||||
highp mat4 xform; //to decal transform
|
||||
highp vec3 inv_extents;
|
||||
mediump float albedo_mix;
|
||||
highp vec4 albedo_rect;
|
||||
highp vec4 normal_rect;
|
||||
highp vec4 orm_rect;
|
||||
highp vec4 emission_rect;
|
||||
highp vec4 modulate;
|
||||
mediump float emission_energy;
|
||||
uint mask;
|
||||
mediump float upper_fade;
|
||||
mediump float lower_fade;
|
||||
mediump mat3x4 normal_xform;
|
||||
mediump vec3 normal;
|
||||
mediump float normal_fade;
|
||||
};
|
||||
19
engine/servers/rendering/renderer_rd/shaders/effects/SCsub
Normal file
19
engine/servers/rendering/renderer_rd/shaders/effects/SCsub
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
||||
SConscript("fsr2/SCsub")
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "blur_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "blur_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// We do not apply our color scale for our mobile renderer here, we'll leave our colors at half brightness and apply scale in the tonemap raster.
|
||||
|
||||
#ifdef MODE_MIPMAP
|
||||
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(-0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, -0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(0.5, 0.5) * pix_size);
|
||||
color += texture(source_color, uv_interp + vec2(-0.5, 0.5) * pix_size);
|
||||
frag_color = color / 4.0;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
|
||||
// For Gaussian Blur we use 13 taps in a single pass instead of 12 taps over 2 passes.
|
||||
// This minimizes the number of times we change framebuffers which is very important for mobile.
|
||||
// Source: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
|
||||
vec4 A = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, -1.0));
|
||||
vec4 B = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, -1.0));
|
||||
vec4 C = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, -1.0));
|
||||
vec4 D = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, -0.5));
|
||||
vec4 E = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, -0.5));
|
||||
vec4 F = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 0.0));
|
||||
vec4 G = texture(source_color, uv_interp);
|
||||
vec4 H = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 0.0));
|
||||
vec4 I = texture(source_color, uv_interp + blur.pixel_size * vec2(-0.5, 0.5));
|
||||
vec4 J = texture(source_color, uv_interp + blur.pixel_size * vec2(0.5, 0.5));
|
||||
vec4 K = texture(source_color, uv_interp + blur.pixel_size * vec2(-1.0, 1.0));
|
||||
vec4 L = texture(source_color, uv_interp + blur.pixel_size * vec2(0.0, 1.0));
|
||||
vec4 M = texture(source_color, uv_interp + blur.pixel_size * vec2(1.0, 1.0));
|
||||
|
||||
float base_weight = 0.5 / 4.0;
|
||||
float lesser_weight = 0.125 / 4.0;
|
||||
|
||||
frag_color = (D + E + I + J) * base_weight;
|
||||
frag_color += (A + B + G + F) * lesser_weight;
|
||||
frag_color += (B + C + H + G) * lesser_weight;
|
||||
frag_color += (F + G + L + K) * lesser_weight;
|
||||
frag_color += (G + H + M + L) * lesser_weight;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_GLOW
|
||||
|
||||
//Glow uses larger sigma 1 for a more rounded blur effect
|
||||
|
||||
#define GLOW_ADD(m_ofs, m_mult) \
|
||||
{ \
|
||||
vec2 ofs = uv_interp + m_ofs * pix_size; \
|
||||
vec4 c = texture(source_color, ofs) * m_mult; \
|
||||
if (any(lessThan(ofs, vec2(0.0))) || any(greaterThan(ofs, vec2(1.0)))) { \
|
||||
c *= 0.0; \
|
||||
} \
|
||||
color += c; \
|
||||
}
|
||||
|
||||
if (bool(blur.flags & FLAG_HORIZONTAL)) {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
pix_size *= 0.5; //reading from larger buffer, so use more samples
|
||||
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.174938;
|
||||
GLOW_ADD(vec2(1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(3.0, 0.0), 0.106595);
|
||||
GLOW_ADD(vec2(-1.0, 0.0), 0.165569);
|
||||
GLOW_ADD(vec2(-2.0, 0.0), 0.140367);
|
||||
GLOW_ADD(vec2(-3.0, 0.0), 0.106595);
|
||||
|
||||
// only do this in the horizontal pass, if we also do this in the vertical pass we're doubling up.
|
||||
color *= blur.glow_strength;
|
||||
|
||||
frag_color = color;
|
||||
} else {
|
||||
vec2 pix_size = blur.pixel_size;
|
||||
vec4 color = texture(source_color, uv_interp + vec2(0.0, 0.0) * pix_size) * 0.288713;
|
||||
GLOW_ADD(vec2(0.0, 1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, 2.0), 0.122581);
|
||||
GLOW_ADD(vec2(0.0, -1.0), 0.233062);
|
||||
GLOW_ADD(vec2(0.0, -2.0), 0.122581);
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
||||
#undef GLOW_ADD
|
||||
|
||||
if (bool(blur.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// In the first pass bring back to correct color range else we're applying the wrong threshold
|
||||
// in subsequent passes we can use it as is as we'd just be undoing it right after.
|
||||
frag_color *= blur.luminance_multiplier;
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
|
||||
frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_scale;
|
||||
#endif
|
||||
frag_color *= blur.glow_exposure;
|
||||
|
||||
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
|
||||
float feedback = max(smoothstep(blur.glow_hdr_threshold, blur.glow_hdr_threshold + blur.glow_hdr_scale, luminance), blur.glow_bloom);
|
||||
|
||||
frag_color = min(frag_color * feedback, vec4(blur.glow_luminance_cap)) / blur.luminance_multiplier;
|
||||
}
|
||||
|
||||
#endif // MODE_GAUSSIAN_GLOW
|
||||
|
||||
#ifdef MODE_COPY
|
||||
vec4 color = textureLod(source_color, uv_interp, 0.0);
|
||||
frag_color = color;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#define FLAG_HORIZONTAL (1 << 0)
|
||||
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 1)
|
||||
#define FLAG_GLOW_FIRST_PASS (1 << 2)
|
||||
|
||||
layout(push_constant, std430) uniform Blur {
|
||||
vec2 pixel_size; // 08 - 08
|
||||
uint flags; // 04 - 12
|
||||
uint pad; // 04 - 16
|
||||
|
||||
// Glow.
|
||||
float glow_strength; // 04 - 20
|
||||
float glow_bloom; // 04 - 24
|
||||
float glow_hdr_threshold; // 04 - 28
|
||||
float glow_hdr_scale; // 04 - 32
|
||||
|
||||
float glow_exposure; // 04 - 36
|
||||
float glow_white; // 04 - 40
|
||||
float glow_luminance_cap; // 04 - 44
|
||||
float glow_auto_exposure_scale; // 04 - 48
|
||||
|
||||
float luminance_multiplier; // 04 - 52
|
||||
float res1; // 04 - 56
|
||||
float res2; // 04 - 60
|
||||
float res3; // 04 - 64
|
||||
}
|
||||
blur;
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
|
||||
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_depth;
|
||||
#endif
|
||||
|
||||
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL) || defined(MODE_BOKEH_CIRCULAR)
|
||||
layout(set = 1, binding = 0) uniform sampler2D color_texture;
|
||||
layout(rgba16f, set = 0, binding = 0) uniform restrict writeonly image2D bokeh_image;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
layout(rgba16f, set = 0, binding = 0) uniform restrict image2D color_image;
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
|
||||
#endif
|
||||
|
||||
// based on https://www.shadertoy.com/view/Xd3GDl
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
|
||||
float get_depth_at_pos(vec2 uv) {
|
||||
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
|
||||
if (params.orthogonal) {
|
||||
depth = -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
|
||||
} else {
|
||||
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
float get_blur_size(float depth) {
|
||||
if (params.blur_near_active && depth < params.blur_near_begin) {
|
||||
if (params.use_physical_near) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_near_begin - depth);
|
||||
return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative.
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative.
|
||||
}
|
||||
}
|
||||
|
||||
if (params.blur_far_active && depth > params.blur_far_begin) {
|
||||
if (params.use_physical_far) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_far_begin - depth);
|
||||
return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP;
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
|
||||
|
||||
vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
|
||||
dir *= pixel_size;
|
||||
vec4 color = texture(color_texture, uv);
|
||||
|
||||
vec4 accum = color;
|
||||
float total = 1.0;
|
||||
|
||||
float blur_scale = params.blur_size / float(params.blur_steps);
|
||||
|
||||
if (params.use_jitter) {
|
||||
uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
|
||||
}
|
||||
|
||||
for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
float radius = float(i) * blur_scale;
|
||||
vec2 suv = uv + dir * radius;
|
||||
radius = abs(radius);
|
||||
|
||||
vec4 sample_color = texture(color_texture, suv);
|
||||
float limit;
|
||||
|
||||
if (sample_color.a < color.a) {
|
||||
limit = abs(sample_color.a);
|
||||
} else {
|
||||
limit = abs(color.a);
|
||||
}
|
||||
|
||||
limit -= DEPTH_GAP;
|
||||
|
||||
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
|
||||
|
||||
accum += mix(color, sample_color, m);
|
||||
|
||||
total += 1.0;
|
||||
}
|
||||
|
||||
return accum / total;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
if (any(greaterThan(pos, params.size))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 pixel_size = 1.0 / vec2(params.size);
|
||||
vec2 uv = vec2(pos) / vec2(params.size);
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
uv += pixel_size * 0.5;
|
||||
//precompute size in alpha channel
|
||||
float depth = get_depth_at_pos(uv);
|
||||
float size = get_blur_size(depth);
|
||||
|
||||
vec4 color = imageLoad(color_image, pos);
|
||||
color.a = size;
|
||||
imageStore(color_image, pos, color);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_BOX
|
||||
|
||||
//pixel_size*=0.5; //resolution is doubled
|
||||
if (params.second_pass || !params.half_size) {
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
} else {
|
||||
uv += pixel_size * 0.25; //half pixel to read centers from full res
|
||||
}
|
||||
|
||||
vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
|
||||
|
||||
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
imageStore(bokeh_image, pos, color);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_HEXAGONAL
|
||||
|
||||
//pixel_size*=0.5; //resolution is doubled
|
||||
if (params.second_pass || !params.half_size) {
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
} else {
|
||||
uv += pixel_size * 0.25; //half pixel to read centers from full res
|
||||
}
|
||||
|
||||
vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
|
||||
|
||||
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
if (params.second_pass) {
|
||||
dir = normalize(vec2(-1.0, 0.577350269189626));
|
||||
|
||||
vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
color.rgb = min(color.rgb, color2.rgb);
|
||||
color.a = (color.a + color2.a) * 0.5;
|
||||
}
|
||||
|
||||
imageStore(bokeh_image, pos, color);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_CIRCULAR
|
||||
|
||||
if (params.half_size) {
|
||||
pixel_size *= 0.5; //resolution is doubled
|
||||
}
|
||||
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
|
||||
vec4 color = texture(color_texture, uv);
|
||||
float initial_blur = color.a;
|
||||
float accum = 1.0;
|
||||
float radius = params.blur_scale;
|
||||
|
||||
for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
|
||||
vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
|
||||
vec4 sample_color = texture(color_texture, suv);
|
||||
float sample_size = abs(sample_color.a);
|
||||
if (sample_color.a > initial_blur) {
|
||||
sample_size = clamp(sample_size, 0.0, abs(initial_blur) * 2.0);
|
||||
}
|
||||
|
||||
float m = smoothstep(radius - 0.5, radius + 0.5, sample_size);
|
||||
color += mix(color / accum, sample_color, m);
|
||||
accum += 1.0;
|
||||
radius += params.blur_scale / radius;
|
||||
}
|
||||
|
||||
color /= accum;
|
||||
|
||||
imageStore(bokeh_image, pos, color);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
|
||||
uv += pixel_size * 0.5;
|
||||
vec4 color = imageLoad(color_image, pos);
|
||||
vec4 bokeh = texture(source_bokeh, uv);
|
||||
|
||||
float mix_amount;
|
||||
if (bokeh.a < color.a) {
|
||||
mix_amount = min(1.0, max(0.0, max(abs(color.a), abs(bokeh.a)) - DEPTH_GAP));
|
||||
} else {
|
||||
mix_amount = min(1.0, max(0.0, abs(color.a) - DEPTH_GAP));
|
||||
}
|
||||
|
||||
color.rgb = mix(color.rgb, bokeh.rgb, mix_amount); //blend between hires and lowres
|
||||
|
||||
color.a = 0; //reset alpha
|
||||
imageStore(color_image, pos, color);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
layout(push_constant, std430) uniform Params {
|
||||
ivec2 size;
|
||||
float z_far;
|
||||
float z_near;
|
||||
|
||||
bool orthogonal;
|
||||
float blur_size;
|
||||
float blur_scale;
|
||||
int blur_steps;
|
||||
|
||||
bool blur_near_active;
|
||||
float blur_near_begin;
|
||||
float blur_near_end;
|
||||
bool blur_far_active;
|
||||
|
||||
float blur_far_begin;
|
||||
float blur_far_end;
|
||||
bool second_pass;
|
||||
bool half_size;
|
||||
|
||||
bool use_jitter;
|
||||
float jitter_seed;
|
||||
bool use_physical_near;
|
||||
bool use_physical_far;
|
||||
|
||||
float blur_size_near;
|
||||
float blur_size_far;
|
||||
uint pad[2];
|
||||
}
|
||||
params;
|
||||
|
||||
//used to work around downsampling filter
|
||||
#define DEPTH_GAP 0.0
|
||||
|
||||
const float GOLDEN_ANGLE = 2.39996323;
|
||||
|
||||
//note: uniform pdf rand [0;1[
|
||||
float hash12n(vec2 p) {
|
||||
p = fract(p * vec2(5.3987, 5.4421));
|
||||
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
|
||||
return fract(p.x * p.y * 95.4307);
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
// old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx
|
||||
// https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982
|
||||
//vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
//gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
//uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
|
||||
vec2 vertex_base;
|
||||
if (gl_VertexIndex == 0) {
|
||||
vertex_base = vec2(-1.0, -1.0);
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
vertex_base = vec2(-1.0, 3.0);
|
||||
} else {
|
||||
vertex_base = vec2(3.0, -1.0);
|
||||
}
|
||||
gl_Position = vec4(vertex_base, 0.0, 1.0);
|
||||
uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "bokeh_dof_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
layout(location = 0) out float weight;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_depth;
|
||||
#else
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
layout(location = 1) out float weight;
|
||||
#endif
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_weight;
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
layout(set = 2, binding = 0) uniform sampler2D original_weight;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//DOF
|
||||
// Bokeh single pass implementation based on https://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
|
||||
float get_depth_at_pos(vec2 uv) {
|
||||
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
|
||||
if (params.orthogonal) {
|
||||
depth = -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
|
||||
} else {
|
||||
depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
float get_blur_size(float depth) {
|
||||
if (params.blur_near_active && depth < params.blur_near_begin) {
|
||||
if (params.use_physical_near) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_near_begin - depth);
|
||||
return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative.
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative.
|
||||
}
|
||||
}
|
||||
|
||||
if (params.blur_far_active && depth > params.blur_far_begin) {
|
||||
if (params.use_physical_far) {
|
||||
// Physically-based.
|
||||
float d = abs(params.blur_far_begin - depth);
|
||||
return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP;
|
||||
} else {
|
||||
// Non-physically-based.
|
||||
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MODE_BOKEH_BOX) || defined(MODE_BOKEH_HEXAGONAL)
|
||||
|
||||
vec4 weighted_filter_dir(vec2 dir, vec2 uv, vec2 pixel_size) {
|
||||
dir *= pixel_size;
|
||||
vec4 color = texture(source_color, uv);
|
||||
color.a = texture(source_weight, uv).r;
|
||||
|
||||
vec4 accum = color;
|
||||
float total = 1.0;
|
||||
|
||||
float blur_scale = params.blur_size / float(params.blur_steps);
|
||||
|
||||
if (params.use_jitter) {
|
||||
uv += dir * (hash12n(uv + params.jitter_seed) - 0.5);
|
||||
}
|
||||
|
||||
for (int i = -params.blur_steps; i <= params.blur_steps; i++) {
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
float radius = float(i) * blur_scale;
|
||||
vec2 suv = uv + dir * radius;
|
||||
radius = abs(radius);
|
||||
|
||||
vec4 sample_color = texture(source_color, suv);
|
||||
sample_color.a = texture(source_weight, suv).r;
|
||||
float limit;
|
||||
|
||||
if (sample_color.a < color.a) {
|
||||
limit = abs(sample_color.a);
|
||||
} else {
|
||||
limit = abs(color.a);
|
||||
}
|
||||
|
||||
limit -= DEPTH_GAP;
|
||||
|
||||
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
|
||||
|
||||
accum += mix(color, sample_color, m);
|
||||
|
||||
total += 1.0;
|
||||
}
|
||||
|
||||
return accum / total;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec2 pixel_size = 1.0 / vec2(params.size);
|
||||
vec2 uv = uv_interp;
|
||||
|
||||
#ifdef MODE_GEN_BLUR_SIZE
|
||||
uv += pixel_size * 0.5;
|
||||
float center_depth = get_depth_at_pos(uv);
|
||||
weight = get_blur_size(center_depth);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_BOX
|
||||
//pixel_size*=0.5; //resolution is doubled
|
||||
if (params.second_pass || !params.half_size) {
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
} else {
|
||||
uv += pixel_size * 0.25; //half pixel to read centers from full res
|
||||
}
|
||||
|
||||
float alpha = texture(source_color, uv).a; // retain this
|
||||
vec2 dir = (params.second_pass ? vec2(0.0, 1.0) : vec2(1.0, 0.0));
|
||||
|
||||
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color.a;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_HEXAGONAL
|
||||
|
||||
//pixel_size*=0.5; //resolution is doubled
|
||||
if (params.second_pass || !params.half_size) {
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
} else {
|
||||
uv += pixel_size * 0.25; //half pixel to read centers from full res
|
||||
}
|
||||
|
||||
float alpha = texture(source_color, uv).a; // retain this
|
||||
|
||||
vec2 dir = (params.second_pass ? normalize(vec2(1.0, 0.577350269189626)) : vec2(0.0, 1.0));
|
||||
|
||||
vec4 color = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
if (params.second_pass) {
|
||||
dir = normalize(vec2(-1.0, 0.577350269189626));
|
||||
|
||||
vec4 color2 = weighted_filter_dir(dir, uv, pixel_size);
|
||||
|
||||
color.rgb = min(color.rgb, color2.rgb);
|
||||
color.a = (color.a + color2.a) * 0.5;
|
||||
}
|
||||
|
||||
frag_color = color;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color.a;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_BOKEH_CIRCULAR
|
||||
if (params.half_size) {
|
||||
pixel_size *= 0.5; //resolution is doubled
|
||||
}
|
||||
|
||||
uv += pixel_size * 0.5; //half pixel to read centers
|
||||
|
||||
vec4 color = texture(source_color, uv);
|
||||
float alpha = color.a; // retain this
|
||||
color.a = texture(source_weight, uv).r;
|
||||
|
||||
vec4 color_accum = color;
|
||||
float accum = 1.0;
|
||||
|
||||
float radius = params.blur_scale;
|
||||
for (float ang = 0.0; radius < params.blur_size; ang += GOLDEN_ANGLE) {
|
||||
vec2 uv_adj = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius;
|
||||
|
||||
vec4 sample_color = texture(source_color, uv_adj);
|
||||
sample_color.a = texture(source_weight, uv_adj).r;
|
||||
|
||||
float limit = abs(sample_color.a);
|
||||
if (sample_color.a > color.a) {
|
||||
limit = clamp(limit, 0.0, abs(color.a) * 2.0);
|
||||
}
|
||||
|
||||
limit -= DEPTH_GAP;
|
||||
|
||||
float m = smoothstep(radius - 0.5, radius + 0.5, limit);
|
||||
color_accum += mix(color_accum / accum, sample_color, m);
|
||||
accum += 1.0;
|
||||
|
||||
radius += params.blur_scale / radius;
|
||||
}
|
||||
|
||||
color_accum = color_accum / accum;
|
||||
|
||||
frag_color.rgb = color_accum.rgb;
|
||||
frag_color.a = alpha; // attempt to retain this in case we have a transparent background, ignored if half_size
|
||||
#ifdef OUTPUT_WEIGHT
|
||||
weight = color_accum.a;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODE_COMPOSITE_BOKEH
|
||||
frag_color.rgb = texture(source_color, uv).rgb;
|
||||
|
||||
float center_weigth = texture(source_weight, uv).r;
|
||||
float sample_weight = texture(original_weight, uv).r;
|
||||
|
||||
float mix_amount;
|
||||
if (sample_weight < center_weigth) {
|
||||
mix_amount = min(1.0, max(0.0, max(abs(center_weigth), abs(sample_weight)) - DEPTH_GAP));
|
||||
} else {
|
||||
mix_amount = min(1.0, max(0.0, abs(center_weigth) - DEPTH_GAP));
|
||||
}
|
||||
|
||||
// let alpha blending take care of mixing
|
||||
frag_color.a = mix_amount;
|
||||
#endif
|
||||
}
|
||||
285
engine/servers/rendering/renderer_rd/shaders/effects/copy.glsl
Normal file
285
engine/servers/rendering/renderer_rd/shaders/effects/copy.glsl
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
#define FLAG_HORIZONTAL (1 << 0)
|
||||
#define FLAG_USE_BLUR_SECTION (1 << 1)
|
||||
#define FLAG_USE_ORTHOGONAL_PROJECTION (1 << 2)
|
||||
#define FLAG_DOF_NEAR_FIRST_TAP (1 << 3)
|
||||
#define FLAG_GLOW_FIRST_PASS (1 << 4)
|
||||
#define FLAG_FLIP_Y (1 << 5)
|
||||
#define FLAG_FORCE_LUMINANCE (1 << 6)
|
||||
#define FLAG_COPY_ALL_SOURCE (1 << 7)
|
||||
#define FLAG_ALPHA_TO_ONE (1 << 8)
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
ivec4 section;
|
||||
ivec2 target;
|
||||
uint flags;
|
||||
uint 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;
|
||||
uint pad2[2];
|
||||
|
||||
vec4 set_color;
|
||||
}
|
||||
params;
|
||||
|
||||
#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
|
||||
layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
|
||||
#elif defined(MODE_CUBEMAP_TO_PANORAMA)
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_color;
|
||||
#elif !defined(MODE_SET_COLOR)
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#endif
|
||||
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
|
||||
#endif
|
||||
|
||||
#if defined(MODE_LINEARIZE_DEPTH_COPY) || defined(MODE_SIMPLE_COPY_DEPTH)
|
||||
layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
|
||||
#elif defined(DST_IMAGE_8BIT)
|
||||
layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
|
||||
#else
|
||||
layout(rgba16f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
shared vec4 local_cache[256];
|
||||
shared vec4 temp_cache[128];
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
// Pixel being shaded
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
#ifndef MODE_GAUSSIAN_BLUR // Gaussian blur needs the extra threads
|
||||
if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODE_MIPMAP
|
||||
|
||||
ivec2 base_pos = (pos + params.section.xy) << 1;
|
||||
vec4 color = texelFetch(source_color, base_pos, 0);
|
||||
color += texelFetch(source_color, base_pos + ivec2(0, 1), 0);
|
||||
color += texelFetch(source_color, base_pos + ivec2(1, 0), 0);
|
||||
color += texelFetch(source_color, base_pos + ivec2(1, 1), 0);
|
||||
color /= 4.0;
|
||||
color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isinf(color));
|
||||
color = mix(color, vec4(100.0, 100.0, 100.0, 1.0), isnan(color));
|
||||
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
#endif
|
||||
|
||||
#ifdef MODE_GAUSSIAN_BLUR
|
||||
|
||||
// First pass copy texture into 16x16 local memory for every 8x8 thread block
|
||||
vec2 quad_center_uv = clamp(vec2(params.section.xy + gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
|
||||
uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16;
|
||||
|
||||
local_cache[dest_index] = textureLod(source_color, quad_center_uv, 0);
|
||||
local_cache[dest_index + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0);
|
||||
local_cache[dest_index + 16] = textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0);
|
||||
local_cache[dest_index + 16 + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0);
|
||||
|
||||
#ifdef MODE_GLOW
|
||||
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// Tonemap initial samples to reduce weight of fireflies: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
vec3 tonemap_col = vec3(0.299, 0.587, 0.114) / max(params.glow_luminance_cap, 6.0);
|
||||
local_cache[dest_index] /= 1.0 + dot(local_cache[dest_index].rgb, tonemap_col);
|
||||
local_cache[dest_index + 1] /= 1.0 + dot(local_cache[dest_index + 1].rgb, tonemap_col);
|
||||
local_cache[dest_index + 16] /= 1.0 + dot(local_cache[dest_index + 16].rgb, tonemap_col);
|
||||
local_cache[dest_index + 16 + 1] /= 1.0 + dot(local_cache[dest_index + 16 + 1].rgb, tonemap_col);
|
||||
}
|
||||
const float kernel[5] = { 0.2024, 0.1790, 0.1240, 0.0672, 0.0285 };
|
||||
#else
|
||||
// Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect.
|
||||
const float kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
|
||||
#endif
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// Horizontal pass. Needs to copy into 8x16 chunk of local memory so vertical pass has full resolution
|
||||
uint read_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 32 + 4;
|
||||
vec4 color_top = vec4(0.0);
|
||||
color_top += local_cache[read_index] * kernel[0];
|
||||
color_top += local_cache[read_index + 1] * kernel[1];
|
||||
color_top += local_cache[read_index + 2] * kernel[2];
|
||||
color_top += local_cache[read_index + 3] * kernel[3];
|
||||
color_top += local_cache[read_index - 1] * kernel[1];
|
||||
color_top += local_cache[read_index - 2] * kernel[2];
|
||||
color_top += local_cache[read_index - 3] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color_top += local_cache[read_index + 4] * kernel[4];
|
||||
color_top += local_cache[read_index - 4] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
vec4 color_bottom = vec4(0.0);
|
||||
color_bottom += local_cache[read_index + 16] * kernel[0];
|
||||
color_bottom += local_cache[read_index + 1 + 16] * kernel[1];
|
||||
color_bottom += local_cache[read_index + 2 + 16] * kernel[2];
|
||||
color_bottom += local_cache[read_index + 3 + 16] * kernel[3];
|
||||
color_bottom += local_cache[read_index - 1 + 16] * kernel[1];
|
||||
color_bottom += local_cache[read_index - 2 + 16] * kernel[2];
|
||||
color_bottom += local_cache[read_index - 3 + 16] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color_bottom += local_cache[read_index + 4 + 16] * kernel[4];
|
||||
color_bottom += local_cache[read_index - 4 + 16] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
// rotate samples to take advantage of cache coherency
|
||||
uint write_index = gl_LocalInvocationID.y * 2 + gl_LocalInvocationID.x * 16;
|
||||
|
||||
temp_cache[write_index] = color_top;
|
||||
temp_cache[write_index + 1] = color_bottom;
|
||||
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// If destination outside of texture, can stop doing work now
|
||||
if (any(greaterThanEqual(pos, params.section.zw))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Vertical pass
|
||||
uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4;
|
||||
vec4 color = vec4(0.0);
|
||||
|
||||
color += temp_cache[index] * kernel[0];
|
||||
color += temp_cache[index + 1] * kernel[1];
|
||||
color += temp_cache[index + 2] * kernel[2];
|
||||
color += temp_cache[index + 3] * kernel[3];
|
||||
color += temp_cache[index - 1] * kernel[1];
|
||||
color += temp_cache[index - 2] * kernel[2];
|
||||
color += temp_cache[index - 3] * kernel[3];
|
||||
#ifdef MODE_GLOW
|
||||
color += temp_cache[index + 4] * kernel[4];
|
||||
color += temp_cache[index - 4] * kernel[4];
|
||||
#endif // MODE_GLOW
|
||||
|
||||
#ifdef MODE_GLOW
|
||||
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
// Undo tonemap to restore range: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
color /= 1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114) / max(params.glow_luminance_cap, 6.0));
|
||||
}
|
||||
|
||||
color *= params.glow_strength;
|
||||
|
||||
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
|
||||
#ifdef GLOW_USE_AUTO_EXPOSURE
|
||||
|
||||
color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_scale;
|
||||
#endif
|
||||
color *= params.glow_exposure;
|
||||
|
||||
float luminance = max(color.r, max(color.g, color.b));
|
||||
float feedback = max(smoothstep(params.glow_hdr_threshold, params.glow_hdr_threshold + params.glow_hdr_scale, luminance), params.glow_bloom);
|
||||
|
||||
color = min(color * feedback, vec4(params.glow_luminance_cap));
|
||||
}
|
||||
#endif // MODE_GLOW
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
|
||||
#endif // MODE_GAUSSIAN_BLUR
|
||||
|
||||
#ifdef MODE_SIMPLE_COPY
|
||||
|
||||
vec4 color;
|
||||
if (bool(params.flags & FLAG_COPY_ALL_SOURCE)) {
|
||||
vec2 uv = vec2(pos) / vec2(params.section.zw);
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
uv.y = 1.0 - uv.y;
|
||||
}
|
||||
color = textureLod(source_color, uv, 0.0);
|
||||
|
||||
} else {
|
||||
color = texelFetch(source_color, pos + params.section.xy, 0);
|
||||
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
pos.y = params.section.w - pos.y - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
|
||||
color.rgb = vec3(max(max(color.r, color.g), color.b));
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
|
||||
color.a = 1.0;
|
||||
}
|
||||
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
|
||||
#endif // MODE_SIMPLE_COPY
|
||||
|
||||
#ifdef MODE_SIMPLE_COPY_DEPTH
|
||||
|
||||
vec4 color = texelFetch(source_color, pos + params.section.xy, 0);
|
||||
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
pos.y = params.section.w - pos.y - 1;
|
||||
}
|
||||
|
||||
imageStore(dest_buffer, pos + params.target, vec4(color.r));
|
||||
|
||||
#endif // MODE_SIMPLE_COPY_DEPTH
|
||||
|
||||
#ifdef MODE_LINEARIZE_DEPTH_COPY
|
||||
|
||||
float depth = texelFetch(source_color, pos + params.section.xy, 0).r;
|
||||
depth = depth * 2.0 - 1.0;
|
||||
depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
|
||||
vec4 color = vec4(depth / params.camera_z_far);
|
||||
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
pos.y = params.section.w - pos.y - 1;
|
||||
}
|
||||
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
#endif // MODE_LINEARIZE_DEPTH_COPY
|
||||
|
||||
#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
vec2 uv = vec2(pos) / vec2(params.section.zw);
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
uv.y = 1.0 - uv.y;
|
||||
}
|
||||
float phi = uv.x * 2.0 * PI;
|
||||
float theta = uv.y * PI;
|
||||
|
||||
vec3 normal;
|
||||
normal.x = sin(phi) * sin(theta) * -1.0;
|
||||
normal.y = cos(theta);
|
||||
normal.z = cos(phi) * sin(theta) * -1.0;
|
||||
|
||||
#ifdef MODE_CUBEMAP_TO_PANORAMA
|
||||
vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
|
||||
#else
|
||||
vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
|
||||
#endif
|
||||
imageStore(dest_buffer, pos + params.target, color);
|
||||
#endif // defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
|
||||
|
||||
#ifdef MODE_SET_COLOR
|
||||
imageStore(dest_buffer, pos + params.target, params.set_color);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#define FLAG_FLIP_Y (1 << 0)
|
||||
#define FLAG_USE_SECTION (1 << 1)
|
||||
#define FLAG_FORCE_LUMINANCE (1 << 2)
|
||||
#define FLAG_ALPHA_TO_ZERO (1 << 3)
|
||||
#define FLAG_SRGB (1 << 4)
|
||||
#define FLAG_ALPHA_TO_ONE (1 << 5)
|
||||
#define FLAG_LINEAR (1 << 6)
|
||||
#define FLAG_NORMAL (1 << 7)
|
||||
#define FLAG_USE_SRC_SECTION (1 << 8)
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) out vec3 uv_interp;
|
||||
#else
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec4 section;
|
||||
vec2 pixel_size;
|
||||
float luminance_multiplier;
|
||||
uint flags;
|
||||
|
||||
vec4 color;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
|
||||
uv_interp.xy = base_arr[gl_VertexIndex];
|
||||
#ifdef USE_MULTIVIEW
|
||||
uv_interp.z = ViewIndex;
|
||||
#endif
|
||||
vec2 vpos = uv_interp.xy;
|
||||
if (bool(params.flags & FLAG_USE_SECTION)) {
|
||||
vpos = params.section.xy + vpos * params.section.zw;
|
||||
}
|
||||
|
||||
gl_Position = vec4(vpos * 2.0 - 1.0, 0.0, 1.0);
|
||||
|
||||
if (bool(params.flags & FLAG_FLIP_Y)) {
|
||||
uv_interp.y = 1.0 - uv_interp.y;
|
||||
}
|
||||
|
||||
if (bool(params.flags & FLAG_USE_SRC_SECTION)) {
|
||||
uv_interp.xy = params.section.xy + uv_interp.xy * params.section.zw;
|
||||
}
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
#ifdef has_VK_KHR_multiview
|
||||
#extension GL_EXT_multiview : enable
|
||||
#define ViewIndex gl_ViewIndex
|
||||
#else // has_VK_KHR_multiview
|
||||
#define ViewIndex 0
|
||||
#endif // has_VK_KHR_multiview
|
||||
#endif //USE_MULTIVIEW
|
||||
|
||||
#define FLAG_FLIP_Y (1 << 0)
|
||||
#define FLAG_USE_SECTION (1 << 1)
|
||||
#define FLAG_FORCE_LUMINANCE (1 << 2)
|
||||
#define FLAG_ALPHA_TO_ZERO (1 << 3)
|
||||
#define FLAG_SRGB (1 << 4)
|
||||
#define FLAG_ALPHA_TO_ONE (1 << 5)
|
||||
#define FLAG_LINEAR (1 << 6)
|
||||
#define FLAG_NORMAL (1 << 7)
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
vec4 section;
|
||||
vec2 pixel_size;
|
||||
float luminance_multiplier;
|
||||
uint flags;
|
||||
|
||||
vec4 color;
|
||||
}
|
||||
params;
|
||||
|
||||
#ifndef MODE_SET_COLOR
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(location = 0) in vec3 uv_interp;
|
||||
#else
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
#endif
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
layout(set = 1, binding = 0) uniform sampler2DArray source_depth;
|
||||
layout(location = 1) out float depth;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#else /* USE_MULTIVIEW */
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_color;
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
layout(set = 1, binding = 0) uniform sampler2D source_color2;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#endif /* USE_MULTIVIEW */
|
||||
#endif /* !SET_COLOR */
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
vec3 linear_to_srgb(vec3 color) {
|
||||
//if going to srgb, clamp from 0 to 1.
|
||||
color = clamp(color, vec3(0.0), vec3(1.0));
|
||||
const vec3 a = vec3(0.055f);
|
||||
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
|
||||
}
|
||||
|
||||
vec3 srgb_to_linear(vec3 color) {
|
||||
return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef MODE_SET_COLOR
|
||||
frag_color = params.color;
|
||||
#else
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec3 uv = uv_interp;
|
||||
#else
|
||||
vec2 uv = uv_interp;
|
||||
#endif
|
||||
|
||||
#ifdef MODE_PANORAMA_TO_DP
|
||||
// Note, multiview and panorama should not be mixed at this time
|
||||
|
||||
//obtain normal from dual paraboloid uv
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
float side;
|
||||
uv.y = modf(uv.y * 2.0, side);
|
||||
side = side * 2.0 - 1.0;
|
||||
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
|
||||
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
|
||||
normal *= -side;
|
||||
normal = normalize(normal);
|
||||
|
||||
//now convert normal to panorama uv
|
||||
|
||||
vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));
|
||||
|
||||
if (st.x < 0.0) {
|
||||
st.x += M_PI * 2.0;
|
||||
}
|
||||
|
||||
uv = st / vec2(M_PI * 2.0, M_PI);
|
||||
|
||||
if (side < 0.0) {
|
||||
//uv.y = 1.0 - uv.y;
|
||||
uv = 1.0 - uv;
|
||||
}
|
||||
#endif /* MODE_PANORAMA_TO_DP */
|
||||
|
||||
#ifdef USE_MULTIVIEW
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
// In multiview our 2nd input will be our depth map
|
||||
depth = textureLod(source_depth, uv, 0.0).r;
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
|
||||
#else /* USE_MULTIVIEW */
|
||||
vec4 color = textureLod(source_color, uv, 0.0);
|
||||
#ifdef MODE_TWO_SOURCES
|
||||
color += textureLod(source_color2, uv, 0.0);
|
||||
#endif /* MODE_TWO_SOURCES */
|
||||
#endif /* USE_MULTIVIEW */
|
||||
|
||||
if (bool(params.flags & FLAG_FORCE_LUMINANCE)) {
|
||||
color.rgb = vec3(max(max(color.r, color.g), color.b));
|
||||
}
|
||||
if (bool(params.flags & FLAG_ALPHA_TO_ZERO)) {
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
if (bool(params.flags & FLAG_SRGB)) {
|
||||
color.rgb = linear_to_srgb(color.rgb);
|
||||
}
|
||||
if (bool(params.flags & FLAG_ALPHA_TO_ONE)) {
|
||||
color.a = 1.0;
|
||||
}
|
||||
if (bool(params.flags & FLAG_LINEAR)) {
|
||||
color.rgb = srgb_to_linear(color.rgb);
|
||||
}
|
||||
if (bool(params.flags & FLAG_NORMAL)) {
|
||||
color.rgb = normalize(color.rgb * 2.0 - 1.0) * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
frag_color = color / params.luminance_multiplier;
|
||||
#endif // MODE_SET_COLOR
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
|
||||
uv_interp = base_arr[gl_VertexIndex];
|
||||
vec2 screen_pos = uv_interp * params.screen_rect.zw + params.screen_rect.xy;
|
||||
gl_Position = vec4(screen_pos * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cube;
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 uv = uv_interp;
|
||||
vec2 texel_size = abs(params.texel_size);
|
||||
|
||||
uv = clamp(uv * (1.0 + 2.0 * texel_size) - texel_size, vec2(0.0), vec2(1.0));
|
||||
|
||||
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
|
||||
normal.z = 0.5 * (1.0 - dot(normal.xy, normal.xy)); // z = 1/2 - 1/2 * (x^2 + y^2)
|
||||
normal = normalize(normal);
|
||||
|
||||
normal.y = -normal.y; //needs to be flipped to match projection matrix
|
||||
if (params.texel_size.x >= 0.0) { // Sign is used to encode Z flip
|
||||
normal.z = -normal.z;
|
||||
}
|
||||
|
||||
float depth = texture(source_cube, normal).r;
|
||||
|
||||
// absolute values for direction cosines, bigger value equals closer to basis axis
|
||||
vec3 unorm = abs(normal);
|
||||
|
||||
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
|
||||
// x code
|
||||
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
|
||||
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
|
||||
// y code
|
||||
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
|
||||
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
|
||||
// z code
|
||||
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
|
||||
} else {
|
||||
// oh-no we messed up code
|
||||
// has to be
|
||||
unorm = vec3(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
float depth_fix = 1.0 / dot(normal, unorm);
|
||||
|
||||
depth = 2.0 * depth - 1.0;
|
||||
float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
|
||||
// linear_depth equal to view space depth
|
||||
depth = (params.z_far - linear_depth * depth_fix) / params.z_far;
|
||||
gl_FragDepth = depth;
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2016 Activision Publishing, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
|
||||
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
|
||||
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
void main() {
|
||||
uvec3 id = gl_GlobalInvocationID;
|
||||
uint face_size = params.face_size;
|
||||
|
||||
if (id.x < face_size && id.y < face_size) {
|
||||
float inv_face_size = 1.0 / float(face_size);
|
||||
|
||||
float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
|
||||
float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
|
||||
|
||||
float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
|
||||
float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
|
||||
|
||||
float weights[4];
|
||||
weights[0] = calcWeight(u0, v0);
|
||||
weights[1] = calcWeight(u1, v0);
|
||||
weights[2] = calcWeight(u0, v1);
|
||||
weights[3] = calcWeight(u1, v1);
|
||||
|
||||
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
weights[i] = weights[i] * wsum + .125;
|
||||
}
|
||||
|
||||
vec3 dir;
|
||||
vec4 color;
|
||||
switch (id.z) {
|
||||
case 0:
|
||||
get_dir_0(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_0(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_0(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_0(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 1:
|
||||
get_dir_1(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_1(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_1(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_1(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 2:
|
||||
get_dir_2(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_2(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_2(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_2(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 3:
|
||||
get_dir_3(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_3(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_3(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_3(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 4:
|
||||
get_dir_4(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_4(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_4(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_4(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
default:
|
||||
get_dir_5(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_5(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_5(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_5(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
}
|
||||
imageStore(dest_cubemap, ivec3(id), color);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
layout(push_constant, std430) uniform Params {
|
||||
uint face_size;
|
||||
uint face_id; // only used in raster shader
|
||||
}
|
||||
params;
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
void get_dir_0(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = 1.0;
|
||||
dir[1] = v;
|
||||
dir[2] = -u;
|
||||
}
|
||||
|
||||
void get_dir_1(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = -1.0;
|
||||
dir[1] = v;
|
||||
dir[2] = u;
|
||||
}
|
||||
|
||||
void get_dir_2(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = u;
|
||||
dir[1] = 1.0;
|
||||
dir[2] = -v;
|
||||
}
|
||||
|
||||
void get_dir_3(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = u;
|
||||
dir[1] = -1.0;
|
||||
dir[2] = v;
|
||||
}
|
||||
|
||||
void get_dir_4(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = u;
|
||||
dir[1] = v;
|
||||
dir[2] = 1.0;
|
||||
}
|
||||
|
||||
void get_dir_5(out vec3 dir, in float u, in float v) {
|
||||
dir[0] = -u;
|
||||
dir[1] = v;
|
||||
dir[2] = -1.0;
|
||||
}
|
||||
|
||||
float calcWeight(float u, float v) {
|
||||
float val = u * u + v * v + 1.0;
|
||||
return val * sqrt(val);
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2016 Activision Publishing, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0 * float(params.face_size); // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_downsampler_inc.glsl"
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
// Converted from compute shader which uses absolute coordinates.
|
||||
// Could possibly simplify this
|
||||
float face_size = float(params.face_size);
|
||||
float inv_face_size = 1.0 / face_size;
|
||||
vec2 id = floor(uv_interp);
|
||||
|
||||
float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
|
||||
float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
|
||||
|
||||
float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
|
||||
float v1 = (id.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
|
||||
|
||||
float weights[4];
|
||||
weights[0] = calcWeight(u0, v0);
|
||||
weights[1] = calcWeight(u1, v0);
|
||||
weights[2] = calcWeight(u0, v1);
|
||||
weights[3] = calcWeight(u1, v1);
|
||||
|
||||
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
weights[i] = weights[i] * wsum + .125;
|
||||
}
|
||||
|
||||
vec3 dir;
|
||||
vec4 color;
|
||||
switch (params.face_id) {
|
||||
case 0:
|
||||
get_dir_0(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_0(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_0(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_0(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 1:
|
||||
get_dir_1(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_1(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_1(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_1(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 2:
|
||||
get_dir_2(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_2(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_2(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_2(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 3:
|
||||
get_dir_3(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_3(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_3(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_3(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
case 4:
|
||||
get_dir_4(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_4(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_4(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_4(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
default:
|
||||
get_dir_5(dir, u0, v0);
|
||||
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
|
||||
|
||||
get_dir_5(dir, u1, v0);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
|
||||
|
||||
get_dir_5(dir, u0, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
|
||||
|
||||
get_dir_5(dir, u1, v1);
|
||||
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
|
||||
break;
|
||||
}
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright 2016 Activision Publishing, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define GROUP_SIZE 64
|
||||
|
||||
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0;
|
||||
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1;
|
||||
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2;
|
||||
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3;
|
||||
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4;
|
||||
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5;
|
||||
layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6;
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
#define NUM_TAPS 32
|
||||
#else
|
||||
#define NUM_TAPS 8
|
||||
#endif
|
||||
|
||||
#define BASE_RESOLUTION 128
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
|
||||
vec4[7][5][3][24] coeffs;
|
||||
}
|
||||
data;
|
||||
#else
|
||||
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
|
||||
vec4[7][5][6] coeffs;
|
||||
}
|
||||
data;
|
||||
#endif
|
||||
|
||||
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
|
||||
switch (face) {
|
||||
case 0:
|
||||
dir = vec3(1.0, uv[1], -uv[0]);
|
||||
break;
|
||||
case 1:
|
||||
dir = vec3(-1.0, uv[1], uv[0]);
|
||||
break;
|
||||
case 2:
|
||||
dir = vec3(uv[0], 1.0, -uv[1]);
|
||||
break;
|
||||
case 3:
|
||||
dir = vec3(uv[0], -1.0, uv[1]);
|
||||
break;
|
||||
case 4:
|
||||
dir = vec3(uv[0], uv[1], 1.0);
|
||||
break;
|
||||
default:
|
||||
dir = vec3(-uv[0], uv[1], -1.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// INPUT:
|
||||
// id.x = the linear address of the texel (ignoring face)
|
||||
// id.y = the face
|
||||
// -> use to index output texture
|
||||
// id.x = texel x
|
||||
// id.y = texel y
|
||||
// id.z = face
|
||||
uvec3 id = gl_GlobalInvocationID;
|
||||
|
||||
// determine which texel this is
|
||||
#ifndef USE_TEXTURE_ARRAY
|
||||
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
|
||||
int mip_level = 0;
|
||||
if (id.x < (128 * 128)) {
|
||||
mip_level = 0;
|
||||
} else if (id.x < (128 * 128 + 64 * 64)) {
|
||||
mip_level = 1;
|
||||
id.x -= (128 * 128);
|
||||
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) {
|
||||
mip_level = 2;
|
||||
id.x -= (128 * 128 + 64 * 64);
|
||||
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) {
|
||||
mip_level = 3;
|
||||
id.x -= (128 * 128 + 64 * 64 + 32 * 32);
|
||||
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) {
|
||||
mip_level = 4;
|
||||
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16);
|
||||
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) {
|
||||
mip_level = 5;
|
||||
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8);
|
||||
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) {
|
||||
mip_level = 6;
|
||||
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
int res = BASE_RESOLUTION >> mip_level;
|
||||
#else // Using Texture Arrays so all levels are the same resolution
|
||||
int res = BASE_RESOLUTION;
|
||||
int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION));
|
||||
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
|
||||
#endif
|
||||
|
||||
// determine dir / pos for the texel
|
||||
vec3 dir, adir, frameZ;
|
||||
{
|
||||
id.z = id.y;
|
||||
id.y = id.x / res;
|
||||
id.x -= id.y * res;
|
||||
|
||||
vec2 uv;
|
||||
uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0;
|
||||
uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0;
|
||||
|
||||
get_dir(dir, uv, id.z);
|
||||
frameZ = normalize(dir);
|
||||
|
||||
adir = abs(dir);
|
||||
}
|
||||
|
||||
// GGX gather colors
|
||||
vec4 color = vec4(0.0);
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
|
||||
const int otherAxis1 = 2 - (axis >> 1);
|
||||
|
||||
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
|
||||
if (frameweight > 0.0) {
|
||||
// determine frame
|
||||
vec3 UpVector;
|
||||
switch (axis) {
|
||||
case 0:
|
||||
UpVector = vec3(1, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
UpVector = vec3(0, 1, 0);
|
||||
break;
|
||||
default:
|
||||
UpVector = vec3(0, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
vec3 frameX = normalize(cross(UpVector, frameZ));
|
||||
vec3 frameY = cross(frameZ, frameX);
|
||||
|
||||
// calculate parametrization for polynomial
|
||||
float Nx = dir[otherAxis0];
|
||||
float Ny = dir[otherAxis1];
|
||||
float Nz = adir[axis];
|
||||
|
||||
float NmaxXY = max(abs(Ny), abs(Nx));
|
||||
Nx /= NmaxXY;
|
||||
Ny /= NmaxXY;
|
||||
|
||||
float theta;
|
||||
if (Ny < Nx) {
|
||||
if (Ny <= -0.999)
|
||||
theta = Nx;
|
||||
else
|
||||
theta = Ny;
|
||||
} else {
|
||||
if (Ny >= 0.999)
|
||||
theta = -Nx;
|
||||
else
|
||||
theta = -Ny;
|
||||
}
|
||||
|
||||
float phi;
|
||||
if (Nz <= -0.999)
|
||||
phi = -NmaxXY;
|
||||
else if (Nz >= 0.999)
|
||||
phi = NmaxXY;
|
||||
else
|
||||
phi = Nz;
|
||||
|
||||
float theta2 = theta * theta;
|
||||
float phi2 = phi * phi;
|
||||
|
||||
// sample
|
||||
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
|
||||
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
vec4 coeffsDir0[3];
|
||||
vec4 coeffsDir1[3];
|
||||
vec4 coeffsDir2[3];
|
||||
vec4 coeffsLevel[3];
|
||||
vec4 coeffsWeight[3];
|
||||
|
||||
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
|
||||
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
|
||||
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
|
||||
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
|
||||
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
|
||||
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
|
||||
}
|
||||
|
||||
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
|
||||
// determine sample attributes (dir, weight, mip_level)
|
||||
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
|
||||
|
||||
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
|
||||
|
||||
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
|
||||
#else
|
||||
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
|
||||
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
|
||||
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
|
||||
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
|
||||
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
|
||||
|
||||
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
|
||||
// determine sample attributes (dir, weight, mip_level)
|
||||
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
|
||||
|
||||
float sample_level = coeffsLevel[iSubTap];
|
||||
|
||||
float sample_weight = coeffsWeight[iSubTap];
|
||||
#endif
|
||||
|
||||
sample_weight *= frameweight;
|
||||
|
||||
// adjust for jacobian
|
||||
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
|
||||
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
|
||||
#ifndef USE_TEXTURE_ARRAY
|
||||
sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
|
||||
#endif
|
||||
// sample cubemap
|
||||
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
|
||||
color.w += sample_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color /= color.w;
|
||||
|
||||
// write color
|
||||
color.xyz = max(vec3(0.0), color.xyz);
|
||||
color.w = 1.0;
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
id.xy *= uvec2(2, 2);
|
||||
#endif
|
||||
|
||||
switch (mip_level) {
|
||||
case 0:
|
||||
imageStore(dest_cubemap0, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
imageStore(dest_cubemap1, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
imageStore(dest_cubemap2, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
imageStore(dest_cubemap3, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
imageStore(dest_cubemap4, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
case 5:
|
||||
imageStore(dest_cubemap5, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
imageStore(dest_cubemap6, ivec3(id), color);
|
||||
#ifdef USE_TEXTURE_ARRAY
|
||||
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
|
||||
imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
|
||||
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
// Copyright 2016 Activision Publishing, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
int mip_level;
|
||||
uint face_id;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
int mip_level;
|
||||
uint face_id;
|
||||
}
|
||||
params;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
#define NUM_TAPS 32
|
||||
#else
|
||||
#define NUM_TAPS 8
|
||||
#endif
|
||||
|
||||
#define BASE_RESOLUTION 128
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
|
||||
vec4[7][5][3][24] coeffs;
|
||||
}
|
||||
data;
|
||||
#else
|
||||
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
|
||||
vec4[7][5][6] coeffs;
|
||||
}
|
||||
data;
|
||||
#endif
|
||||
|
||||
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
|
||||
switch (face) {
|
||||
case 0:
|
||||
dir = vec3(1.0, uv[1], -uv[0]);
|
||||
break;
|
||||
case 1:
|
||||
dir = vec3(-1.0, uv[1], uv[0]);
|
||||
break;
|
||||
case 2:
|
||||
dir = vec3(uv[0], 1.0, -uv[1]);
|
||||
break;
|
||||
case 3:
|
||||
dir = vec3(uv[0], -1.0, uv[1]);
|
||||
break;
|
||||
case 4:
|
||||
dir = vec3(uv[0], uv[1], 1.0);
|
||||
break;
|
||||
default:
|
||||
dir = vec3(-uv[0], uv[1], -1.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// determine dir / pos for the texel
|
||||
vec3 dir, adir, frameZ;
|
||||
{
|
||||
vec2 uv;
|
||||
uv.x = uv_interp.x;
|
||||
uv.y = 1.0 - uv_interp.y;
|
||||
uv = uv * 2.0 - 1.0;
|
||||
|
||||
get_dir(dir, uv, params.face_id);
|
||||
frameZ = normalize(dir);
|
||||
|
||||
adir = abs(dir);
|
||||
}
|
||||
|
||||
// determine which texel this is
|
||||
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
|
||||
int mip_level = 0;
|
||||
|
||||
if (params.mip_level < 0) {
|
||||
// return as is
|
||||
frag_color.rgb = textureLod(source_cubemap, frameZ, 0.0).rgb;
|
||||
frag_color.a = 1.0;
|
||||
return;
|
||||
} else if (params.mip_level > 6) {
|
||||
// maximum level
|
||||
mip_level = 6;
|
||||
} else {
|
||||
mip_level = params.mip_level;
|
||||
}
|
||||
|
||||
// GGX gather colors
|
||||
vec4 color = vec4(0.0);
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
|
||||
const int otherAxis1 = 2 - (axis >> 1);
|
||||
|
||||
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
|
||||
if (frameweight > 0.0) {
|
||||
// determine frame
|
||||
vec3 UpVector;
|
||||
switch (axis) {
|
||||
case 0:
|
||||
UpVector = vec3(1, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
UpVector = vec3(0, 1, 0);
|
||||
break;
|
||||
default:
|
||||
UpVector = vec3(0, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
vec3 frameX = normalize(cross(UpVector, frameZ));
|
||||
vec3 frameY = cross(frameZ, frameX);
|
||||
|
||||
// calculate parametrization for polynomial
|
||||
float Nx = dir[otherAxis0];
|
||||
float Ny = dir[otherAxis1];
|
||||
float Nz = adir[axis];
|
||||
|
||||
float NmaxXY = max(abs(Ny), abs(Nx));
|
||||
Nx /= NmaxXY;
|
||||
Ny /= NmaxXY;
|
||||
|
||||
float theta;
|
||||
if (Ny < Nx) {
|
||||
if (Ny <= -0.999)
|
||||
theta = Nx;
|
||||
else
|
||||
theta = Ny;
|
||||
} else {
|
||||
if (Ny >= 0.999)
|
||||
theta = -Nx;
|
||||
else
|
||||
theta = -Ny;
|
||||
}
|
||||
|
||||
float phi;
|
||||
if (Nz <= -0.999)
|
||||
phi = -NmaxXY;
|
||||
else if (Nz >= 0.999)
|
||||
phi = NmaxXY;
|
||||
else
|
||||
phi = Nz;
|
||||
|
||||
float theta2 = theta * theta;
|
||||
float phi2 = phi * phi;
|
||||
|
||||
// sample
|
||||
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
|
||||
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
|
||||
|
||||
#ifdef USE_HIGH_QUALITY
|
||||
vec4 coeffsDir0[3];
|
||||
vec4 coeffsDir1[3];
|
||||
vec4 coeffsDir2[3];
|
||||
vec4 coeffsLevel[3];
|
||||
vec4 coeffsWeight[3];
|
||||
|
||||
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
|
||||
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
|
||||
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
|
||||
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
|
||||
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
|
||||
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
|
||||
}
|
||||
|
||||
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
|
||||
// determine sample attributes (dir, weight, mip_level)
|
||||
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
|
||||
|
||||
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
|
||||
|
||||
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
|
||||
#else
|
||||
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
|
||||
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
|
||||
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
|
||||
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
|
||||
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
|
||||
|
||||
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
|
||||
// determine sample attributes (dir, weight, mip_level)
|
||||
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
|
||||
|
||||
float sample_level = coeffsLevel[iSubTap];
|
||||
|
||||
float sample_weight = coeffsWeight[iSubTap];
|
||||
#endif
|
||||
|
||||
sample_weight *= frameweight;
|
||||
|
||||
// adjust for jacobian
|
||||
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
|
||||
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
|
||||
// sample cubemap
|
||||
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
|
||||
color.w += sample_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color /= color.w;
|
||||
|
||||
// write color
|
||||
color.xyz = max(vec3(0.0), color.xyz);
|
||||
color.w = 1.0;
|
||||
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define GROUP_SIZE 8
|
||||
|
||||
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cube;
|
||||
|
||||
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
void main() {
|
||||
uvec3 id = gl_GlobalInvocationID;
|
||||
id.z += params.face_id;
|
||||
|
||||
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
|
||||
vec3 N = texelCoordToVec(uv, id.z);
|
||||
|
||||
if (params.use_direct_write) {
|
||||
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
|
||||
} else {
|
||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
|
||||
float roughness2 = params.roughness * params.roughness;
|
||||
float roughness4 = roughness2 * roughness2;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
mat3 T;
|
||||
T[0] = normalize(cross(UpVector, N));
|
||||
T[1] = cross(N, T[0]);
|
||||
T[2] = N;
|
||||
|
||||
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||
|
||||
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||
float NdotH = dot(N, H);
|
||||
vec3 L = (2.0 * NdotH * H - N);
|
||||
|
||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||
|
||||
if (ndotl > 0.0) {
|
||||
float D = DistributionGGX(NdotH, roughness4);
|
||||
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
|
||||
|
||||
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
|
||||
|
||||
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
|
||||
|
||||
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
|
||||
sum.a += ndotl;
|
||||
}
|
||||
}
|
||||
sum /= sum.a;
|
||||
|
||||
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#define M_PI 3.14159265359
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
uint face_id;
|
||||
uint sample_count;
|
||||
float roughness;
|
||||
bool use_direct_write;
|
||||
float face_size;
|
||||
}
|
||||
params;
|
||||
|
||||
vec3 texelCoordToVec(vec2 uv, uint faceID) {
|
||||
mat3 faceUvVectors[6];
|
||||
|
||||
// -x
|
||||
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
|
||||
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
|
||||
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
|
||||
|
||||
// +x
|
||||
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
|
||||
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
|
||||
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
|
||||
|
||||
// -y
|
||||
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
|
||||
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
|
||||
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
|
||||
|
||||
// +y
|
||||
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
|
||||
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
|
||||
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
|
||||
|
||||
// -z
|
||||
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
|
||||
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
|
||||
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
|
||||
|
||||
// +z
|
||||
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
|
||||
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
|
||||
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
|
||||
|
||||
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
|
||||
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
|
||||
return normalize(result);
|
||||
}
|
||||
|
||||
vec3 ImportanceSampleGGX(vec2 xi, float roughness4) {
|
||||
// Compute distribution direction
|
||||
float Phi = 2.0 * M_PI * xi.x;
|
||||
float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
|
||||
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
|
||||
|
||||
// Convert to spherical direction
|
||||
vec3 H;
|
||||
H.x = SinTheta * cos(Phi);
|
||||
H.y = SinTheta * sin(Phi);
|
||||
H.z = CosTheta;
|
||||
|
||||
return H;
|
||||
}
|
||||
|
||||
float DistributionGGX(float NdotH, float roughness4) {
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
|
||||
denom = M_PI * denom * denom;
|
||||
|
||||
return roughness4 / denom;
|
||||
}
|
||||
|
||||
float radicalInverse_VdC(uint bits) {
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
|
||||
}
|
||||
|
||||
vec2 Hammersley(uint i, uint N) {
|
||||
return vec2(float(i) / float(N), radicalInverse_VdC(i));
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "cubemap_roughness_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
layout(set = 0, binding = 0) uniform samplerCube source_cube;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec3 N = texelCoordToVec(uv_interp * 2.0 - 1.0, params.face_id);
|
||||
|
||||
//vec4 color = color_interp;
|
||||
|
||||
if (params.use_direct_write) {
|
||||
frag_color = vec4(texture(source_cube, N).rgb, 1.0);
|
||||
} else {
|
||||
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
|
||||
float roughness2 = params.roughness * params.roughness;
|
||||
float roughness4 = roughness2 * roughness2;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
mat3 T;
|
||||
T[0] = normalize(cross(UpVector, N));
|
||||
T[1] = cross(N, T[0]);
|
||||
T[2] = N;
|
||||
|
||||
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
|
||||
vec2 xi = Hammersley(sampleNum, params.sample_count);
|
||||
|
||||
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
|
||||
float NdotH = dot(N, H);
|
||||
vec3 L = (2.0 * NdotH * H - N);
|
||||
|
||||
float ndotl = clamp(dot(N, L), 0.0, 1.0);
|
||||
|
||||
if (ndotl > 0.0) {
|
||||
float D = DistributionGGX(NdotH, roughness4);
|
||||
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
|
||||
|
||||
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
|
||||
|
||||
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
|
||||
|
||||
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
|
||||
sum.a += ndotl;
|
||||
}
|
||||
}
|
||||
sum /= sum.a;
|
||||
|
||||
frag_color = vec4(sum.rgb, 1.0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "RD_GLSL" in env["BUILDERS"]:
|
||||
# find all include files
|
||||
gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")]
|
||||
|
||||
# Add all FSR2 shader and header files.
|
||||
fsr2_dir = "#thirdparty/amd-fsr2/shaders"
|
||||
gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.h")]
|
||||
gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.glsl")]
|
||||
|
||||
# find all shader code(all glsl files excluding our include files)
|
||||
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
|
||||
|
||||
# make sure we recompile shaders if include files change
|
||||
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"])
|
||||
|
||||
# compile shaders
|
||||
for glsl_file in glsl_files:
|
||||
env.RD_GLSL(glsl_file)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_accumulate_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_autogen_reactive_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_lock_pass.glsl"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_rcas_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "../motion_vector_inc.glsl"
|
||||
#include "thirdparty/amd-fsr2/shaders/ffx_fsr2_tcr_autogen_pass.glsl"
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/**************************************************************************/
|
||||
/* fsr_upscale.glsl */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define A_GPU
|
||||
#define A_GLSL
|
||||
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
#define A_HALF
|
||||
|
||||
#endif
|
||||
|
||||
#include "thirdparty/amd-fsr/ffx_a.h"
|
||||
|
||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D fsr_image;
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_image;
|
||||
|
||||
#define FSR_UPSCALE_PASS_TYPE_EASU 0
|
||||
#define FSR_UPSCALE_PASS_TYPE_RCAS 1
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
float resolution_width;
|
||||
float resolution_height;
|
||||
float upscaled_width;
|
||||
float upscaled_height;
|
||||
float sharpness;
|
||||
int pass;
|
||||
}
|
||||
params;
|
||||
|
||||
AU4 Const0, Const1, Const2, Const3;
|
||||
|
||||
#ifdef MODE_FSR_UPSCALE_FALLBACK
|
||||
|
||||
#define FSR_EASU_F
|
||||
AF4 FsrEasuRF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 0);
|
||||
return res;
|
||||
}
|
||||
AF4 FsrEasuGF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 1);
|
||||
return res;
|
||||
}
|
||||
AF4 FsrEasuBF(AF2 p) {
|
||||
AF4 res = textureGather(source_image, p, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
#define FSR_RCAS_F
|
||||
AF4 FsrRcasLoadF(ASU2 p) {
|
||||
return AF4(texelFetch(source_image, ASU2(p), 0));
|
||||
}
|
||||
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||
|
||||
#else
|
||||
|
||||
#define FSR_EASU_H
|
||||
AH4 FsrEasuRH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 0));
|
||||
return res;
|
||||
}
|
||||
AH4 FsrEasuGH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 1));
|
||||
return res;
|
||||
}
|
||||
AH4 FsrEasuBH(AF2 p) {
|
||||
AH4 res = AH4(textureGather(source_image, p, 2));
|
||||
return res;
|
||||
}
|
||||
|
||||
#define FSR_RCAS_H
|
||||
AH4 FsrRcasLoadH(ASW2 p) {
|
||||
return AH4(texelFetch(source_image, ASU2(p), 0));
|
||||
}
|
||||
void FsrRcasInputH(inout AH1 r, inout AH1 g, inout AH1 b) {}
|
||||
|
||||
#endif
|
||||
|
||||
#include "thirdparty/amd-fsr/ffx_fsr1.h"
|
||||
|
||||
void fsr_easu_pass(AU2 pos) {
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
AH3 Gamma2Color = AH3(0, 0, 0);
|
||||
FsrEasuH(Gamma2Color, pos, Const0, Const1, Const2, Const3);
|
||||
imageStore(fsr_image, ASU2(pos), AH4(Gamma2Color, 1));
|
||||
|
||||
#else
|
||||
|
||||
AF3 Gamma2Color = AF3(0, 0, 0);
|
||||
FsrEasuF(Gamma2Color, pos, Const0, Const1, Const2, Const3);
|
||||
imageStore(fsr_image, ASU2(pos), AF4(Gamma2Color, 1));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void fsr_rcas_pass(AU2 pos) {
|
||||
#ifdef MODE_FSR_UPSCALE_NORMAL
|
||||
|
||||
AH3 Gamma2Color = AH3(0, 0, 0);
|
||||
FsrRcasH(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, pos, Const0);
|
||||
imageStore(fsr_image, ASU2(pos), AH4(Gamma2Color, 1));
|
||||
|
||||
#else
|
||||
|
||||
AF3 Gamma2Color = AF3(0, 0, 0);
|
||||
FsrRcasF(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, pos, Const0);
|
||||
imageStore(fsr_image, ASU2(pos), AF4(Gamma2Color, 1));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void fsr_pass(AU2 pos) {
|
||||
if (params.pass == FSR_UPSCALE_PASS_TYPE_EASU) {
|
||||
fsr_easu_pass(pos);
|
||||
} else if (params.pass == FSR_UPSCALE_PASS_TYPE_RCAS) {
|
||||
fsr_rcas_pass(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Clang does not like unused functions. If ffx_a.h is included in the binary, clang will throw a fit and not compile so we must configure FSR in this shader
|
||||
if (params.pass == FSR_UPSCALE_PASS_TYPE_EASU) {
|
||||
FsrEasuCon(Const0, Const1, Const2, Const3, params.resolution_width, params.resolution_height, params.resolution_width, params.resolution_height, params.upscaled_width, params.upscaled_height);
|
||||
} else if (params.pass == FSR_UPSCALE_PASS_TYPE_RCAS) {
|
||||
FsrRcasCon(Const0, params.sharpness);
|
||||
}
|
||||
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||
|
||||
fsr_pass(gxy);
|
||||
gxy.x += 8u;
|
||||
fsr_pass(gxy);
|
||||
gxy.y += 8u;
|
||||
fsr_pass(gxy);
|
||||
gxy.x -= 8u;
|
||||
fsr_pass(gxy);
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
|
||||
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
|
||||
|
||||
shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE];
|
||||
|
||||
#ifdef READ_TEXTURE
|
||||
|
||||
//use for main texture
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_texture;
|
||||
|
||||
#else
|
||||
|
||||
//use for intermediate textures
|
||||
layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_luminance;
|
||||
|
||||
#endif
|
||||
|
||||
layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_luminance;
|
||||
|
||||
#ifdef WRITE_LUMINANCE
|
||||
layout(set = 2, binding = 0) uniform sampler2D prev_luminance;
|
||||
#endif
|
||||
|
||||
layout(push_constant, std430) uniform Params {
|
||||
ivec2 source_size;
|
||||
float max_luminance;
|
||||
float min_luminance;
|
||||
float exposure_adjust;
|
||||
float pad[3];
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
uint t = gl_LocalInvocationID.y * BLOCK_SIZE + gl_LocalInvocationID.x;
|
||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
||||
|
||||
if (any(lessThan(pos, params.source_size))) {
|
||||
#ifdef READ_TEXTURE
|
||||
vec3 v = texelFetch(source_texture, pos, 0).rgb;
|
||||
tmp_data[t] = max(v.r, max(v.g, v.b));
|
||||
#else
|
||||
tmp_data[t] = imageLoad(source_luminance, pos).r;
|
||||
#endif
|
||||
} else {
|
||||
tmp_data[t] = 0.0;
|
||||
}
|
||||
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
uint size = (BLOCK_SIZE * BLOCK_SIZE) >> 1;
|
||||
|
||||
do {
|
||||
if (t < size) {
|
||||
tmp_data[t] += tmp_data[t + size];
|
||||
}
|
||||
groupMemoryBarrier();
|
||||
barrier();
|
||||
|
||||
size >>= 1;
|
||||
} while (size >= 1);
|
||||
|
||||
if (t == 0) {
|
||||
//compute rect size
|
||||
ivec2 rect_size = min(params.source_size - pos, ivec2(BLOCK_SIZE));
|
||||
float avg = tmp_data[0] / float(rect_size.x * rect_size.y);
|
||||
//float avg = tmp_data[0] / float(BLOCK_SIZE*BLOCK_SIZE);
|
||||
pos /= ivec2(BLOCK_SIZE);
|
||||
#ifdef WRITE_LUMINANCE
|
||||
float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous exposure
|
||||
avg = clamp(prev_lum + (avg - prev_lum) * params.exposure_adjust, params.min_luminance, params.max_luminance);
|
||||
#endif
|
||||
imageStore(dest_luminance, pos, vec4(avg));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/* clang-format off */
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "luminance_reduce_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
void main() {
|
||||
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
|
||||
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
|
||||
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#include "luminance_reduce_raster_inc.glsl"
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
/* clang-format on */
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D source_exposure;
|
||||
|
||||
#ifdef FINAL_PASS
|
||||
layout(set = 1, binding = 0) uniform sampler2D prev_luminance;
|
||||
#endif
|
||||
|
||||
layout(location = 0) out highp float luminance;
|
||||
|
||||
void main() {
|
||||
ivec2 dest_pos = ivec2(uv_interp * settings.dest_size);
|
||||
ivec2 src_pos = ivec2(uv_interp * settings.source_size);
|
||||
|
||||
ivec2 next_pos = (dest_pos + ivec2(1)) * settings.source_size / settings.dest_size;
|
||||
next_pos = max(next_pos, src_pos + ivec2(1)); //so it at least reads one pixel
|
||||
|
||||
highp vec3 source_color = vec3(0.0);
|
||||
for (int i = src_pos.x; i < next_pos.x; i++) {
|
||||
for (int j = src_pos.y; j < next_pos.y; j++) {
|
||||
source_color += texelFetch(source_exposure, ivec2(i, j), 0).rgb;
|
||||
}
|
||||
}
|
||||
|
||||
source_color /= float((next_pos.x - src_pos.x) * (next_pos.y - src_pos.y));
|
||||
|
||||
#ifdef FIRST_PASS
|
||||
luminance = max(source_color.r, max(source_color.g, source_color.b));
|
||||
|
||||
// This formula should be more "accurate" but gave an overexposed result when testing.
|
||||
// Leaving it here so we can revisit it if we want.
|
||||
// luminance = source_color.r * 0.21 + source_color.g * 0.71 + source_color.b * 0.07;
|
||||
#else
|
||||
luminance = source_color.r;
|
||||
#endif
|
||||
|
||||
#ifdef FINAL_PASS
|
||||
// Obtain our target luminance
|
||||
luminance = clamp(luminance, settings.min_luminance, settings.max_luminance);
|
||||
|
||||
// Now smooth to our transition
|
||||
highp float prev_lum = texelFetch(prev_luminance, ivec2(0, 0), 0).r; //1 pixel previous luminance
|
||||
luminance = prev_lum + (luminance - prev_lum) * clamp(settings.exposure_adjust, 0.0, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
layout(push_constant, std430) uniform PushConstant {
|
||||
ivec2 source_size;
|
||||
ivec2 dest_size;
|
||||
|
||||
float exposure_adjust;
|
||||
float min_luminance;
|
||||
float max_luminance;
|
||||
uint pad1;
|
||||
}
|
||||
settings;
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue