Implement support for fragment density maps.

Co-Authored-By: Bastiaan Olij <mux213@gmail.com>
This commit is contained in:
Dario
2024-11-22 11:55:06 -03:00
committed by David Snopek
parent f2cc3f1275
commit 76d709be74
21 changed files with 738 additions and 360 deletions

View File

@ -44,6 +44,8 @@ VRS::VRS() {
Vector<String> vrs_modes;
vrs_modes.push_back("\n"); // VRS_DEFAULT
vrs_modes.push_back("\n#define USE_MULTIVIEW\n"); // VRS_MULTIVIEW
vrs_modes.push_back("\n#define SPLIT_RG\n"); // VRS_RG
vrs_modes.push_back("\n#define SPLIT_RG\n#define USE_MULTIVIEW\n"); // VRS_RG_MULTIVIEW
vrs_shader.shader.initialize(vrs_modes);
@ -80,14 +82,16 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
RD::Uniform u_source_rd_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_rd_texture }));
int mode = 0;
VRSPushConstant push_constant = {};
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;
bool uses_rg_format = RD::get_singleton()->vrs_get_format() == RD::DATA_FORMAT_R8G8_UNORM;
if (uses_rg_format) {
mode = p_multiview ? VRS_RG_MULTIVIEW : VRS_RG;
} else {
mode = p_multiview ? VRS_MULTIVIEW : VRS_DEFAULT;
// Default to 4x4 as it's not possible to query the max fragment size from RenderingDevice. This can be improved to use the largest size
// available if this code is moved over to RenderingDevice at some point.
push_constant.max_texel_factor = 2.0;
}
@ -103,18 +107,8 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi
}
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);
Size2i vrs_texel_size = RD::get_singleton()->vrs_get_texel_size();
return Size2i((p_base_size.x + vrs_texel_size.x - 1) / vrs_texel_size.x, (p_base_size.y + vrs_texel_size.y - 1) / vrs_texel_size.y);
}
void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {

View File

@ -41,6 +41,8 @@ private:
enum VRSMode {
VRS_DEFAULT,
VRS_MULTIVIEW,
VRS_RG,
VRS_RG_MULTIVIEW,
VRS_MAX,
};

View File

@ -167,7 +167,6 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_only_fb(
if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) {
RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE);
return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth, vrs_texture);
} else {
return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), color, depth);
@ -197,7 +196,6 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(
if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) {
RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE);
return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth, vrs_texture);
} else {
return FramebufferCacheRD::get_singleton()->get_cache_multiview(v_count, color, specular, velocity_buffer, depth);

View File

@ -208,9 +208,6 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe
RD::FramebufferPass pass;
pass.color_attachments.push_back(0);
pass.depth_attachment = 1;
if (vrs_texture.is_valid()) {
pass.vrs_attachment = 2;
}
if (use_msaa) {
// Add resolve
@ -231,9 +228,6 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe
RD::FramebufferPass pass;
pass.color_attachments.push_back(0);
pass.depth_attachment = 1;
if (vrs_texture.is_valid()) {
pass.vrs_attachment = 2;
}
if (use_msaa) {
// add resolve
@ -2905,6 +2899,7 @@ static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::Da
attachments.push_back(attachment);
if (p_vrs) {
// VRS attachment.
attachment.samples = RD::TEXTURE_SAMPLES_1;
attachment.format = RenderSceneBuffersRD::get_vrs_format();
attachment.usage_flags = RenderSceneBuffersRD::get_vrs_usage_bits();
@ -2926,10 +2921,6 @@ static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::Da
pass.color_attachments.push_back(0);
pass.depth_attachment = 1;
if (p_vrs) {
pass.vrs_attachment = 2;
}
if (multisampling) {
pass.resolve_attachments.push_back(attachments.size() - 1);
}
@ -2955,7 +2946,8 @@ static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::Da
passes.push_back(blit_pass);
}
return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count);
int32_t vrs_attachment = p_vrs ? 2 : -1;
return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count, vrs_attachment);
}
static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline() {

View File

@ -59,7 +59,6 @@ class FramebufferCacheRD : public Object {
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++) {
@ -84,10 +83,6 @@ class FramebufferCacheRD : public Object {
return false;
}
if (a.vrs_attachment != b.vrs_attachment) {
return false;
}
if (a.color_attachments.size() != b.color_attachments.size()) {
return false;
}

View File

@ -59,7 +59,11 @@ layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif /* USE_MULTIVIEW */
#ifdef SPLIT_RG
layout(location = 0) out vec2 frag_color;
#else
layout(location = 0) out uint frag_color;
#endif
layout(push_constant, std430) uniform Params {
float max_texel_factor;
@ -79,6 +83,10 @@ void main() {
// Input is standardized. R for X, G for Y, 0.0 (0) = 1, 0.33 (85) = 2, 0.66 (170) = 3, 1.0 (255) = 8
vec4 color = textureLod(source_color, uv, 0.0);
#ifdef SPLIT_RG
// Density map for VRS according to VK_EXT_fragment_density_map, we can use as is.
frag_color = max(vec2(1.0f) - color.rg, vec2(1.0f / 255.0f));
#else
// Output image shading rate image for VRS according to VK_KHR_fragment_shading_rate.
color.r = clamp(floor(color.r * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
color.g = clamp(floor(color.g * params.max_texel_factor + 0.1), 0.0, params.max_texel_factor);
@ -94,4 +102,5 @@ void main() {
// Encode to frag_color;
frag_color = int(color.r + 0.1) << 2;
frag_color += int(color.g + 0.1);
#endif
}

View File

@ -756,9 +756,9 @@ uint32_t RenderSceneBuffersRD::get_velocity_usage_bits(bool p_resolve, bool p_ms
}
RD::DataFormat RenderSceneBuffersRD::get_vrs_format() {
return RD::DATA_FORMAT_R8_UINT;
return RD::get_singleton()->vrs_get_format();
}
uint32_t RenderSceneBuffersRD::get_vrs_usage_bits() {
return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT;
}

View File

@ -482,17 +482,15 @@ TextureStorage::TextureStorage() {
}
}
{ //create default VRS
{
// Create default VRS texture.
bool vrs_supported = RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS);
RD::TextureFormat tformat;
tformat.format = RD::DATA_FORMAT_R8_UINT;
tformat.format = vrs_supported ? RD::get_singleton()->vrs_get_format() : RD::DATA_FORMAT_R8_UINT;
tformat.width = 4;
tformat.height = 4;
tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT;
tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | (vrs_supported ? RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT : 0);
tformat.texture_type = RD::TEXTURE_TYPE_2D;
if (!RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS)) {
tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
}
Vector<uint8_t> pv;
pv.resize(4 * 4);

View File

@ -951,22 +951,38 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
ERR_FAIL_COND_V_MSG(required_mipmaps < format.mipmaps, RID(),
"Too many mipmaps requested for texture format and dimensions (" + itos(format.mipmaps) + "), maximum allowed: (" + itos(required_mipmaps) + ").");
uint32_t forced_usage_bits = 0;
if (p_data.size()) {
ERR_FAIL_COND_V_MSG(p_data.size() != (int)format.array_layers, RID(),
"Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(format.array_layers) + ").");
Vector<Vector<uint8_t>> data = p_data;
bool immediate_flush = false;
// If this is a VRS texture, we make sure that it is created with valid initial data. This prevents a crash on Qualcomm Snapdragon XR2 Gen 1
// (used in Quest 2, Quest Pro, Pico 4, HTC Vive XR Elite and others) where the driver will read the texture before we've had time to finish updating it.
if (data.is_empty() && (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) {
immediate_flush = true;
for (uint32_t i = 0; i < format.array_layers; i++) {
uint32_t required_size = get_image_format_required_size(format.format, format.width, format.height, format.depth, format.mipmaps);
Vector<uint8_t> layer;
layer.resize(required_size);
layer.fill(255);
data.push_back(layer);
}
}
uint32_t forced_usage_bits = _texture_vrs_method_to_usage_bits();
if (data.size()) {
ERR_FAIL_COND_V_MSG(data.size() != (int)format.array_layers, RID(),
"Default supplied data for image format is of invalid length (" + itos(data.size()) + "), should be (" + itos(format.array_layers) + ").");
for (uint32_t i = 0; i < format.array_layers; i++) {
uint32_t required_size = get_image_format_required_size(format.format, format.width, format.height, format.depth, format.mipmaps);
ERR_FAIL_COND_V_MSG((uint32_t)p_data[i].size() != required_size, RID(),
"Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ").");
ERR_FAIL_COND_V_MSG((uint32_t)data[i].size() != required_size, RID(),
"Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(data[i].size()) + ") than what is required by the format (" + itos(required_size) + ").");
}
ERR_FAIL_COND_V_MSG(format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, RID(),
"Textures created as depth attachments can't be initialized with data directly. Use RenderingDevice::texture_update() instead.");
if (!(format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT)) {
forced_usage_bits = TEXTURE_USAGE_CAN_UPDATE_BIT;
forced_usage_bits |= TEXTURE_USAGE_CAN_UPDATE_BIT;
}
}
@ -993,7 +1009,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image.");
}
if ((format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && !supported_usage.has_flag(TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) {
ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment.");
ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as variable shading rate attachment.");
}
}
@ -1035,7 +1051,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
texture.usage_flags = format.usage_bits & ~forced_usage_bits;
texture.samples = format.samples;
texture.allowed_shared_formats = format.shareable_formats;
texture.has_initial_data = !p_data.is_empty();
texture.has_initial_data = !data.is_empty();
if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT);
@ -1051,8 +1067,8 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
texture.bound = false;
// Textures are only assumed to be immutable if they have initial data and none of the other bits that indicate write usage are enabled.
bool texture_mutable_by_default = texture.usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_STORAGE_BIT | TEXTURE_USAGE_STORAGE_ATOMIC_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT);
if (p_data.is_empty() || texture_mutable_by_default) {
bool texture_mutable_by_default = texture.usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_STORAGE_BIT | TEXTURE_USAGE_STORAGE_ATOMIC_BIT);
if (data.is_empty() || texture_mutable_by_default) {
_texture_make_mutable(&texture, RID());
}
@ -1063,9 +1079,9 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
if (p_data.size()) {
if (data.size()) {
for (uint32_t i = 0; i < p_format.array_layers; i++) {
_texture_initialize(id, i, p_data[i]);
_texture_initialize(id, i, data[i], immediate_flush);
}
if (texture.draw_tracker != nullptr) {
@ -1399,7 +1415,7 @@ uint32_t RenderingDevice::_texture_alignment(Texture *p_texture) const {
return STEPIFY(alignment, driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT));
}
Error RenderingDevice::_texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data) {
Error RenderingDevice::_texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_immediate_flush) {
Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(texture, ERR_INVALID_PARAMETER);
@ -1531,6 +1547,12 @@ Error RenderingDevice::_texture_initialize(RID p_texture, uint32_t p_layer, cons
transfer_worker->texture_barriers.push_back(tb);
}
if (p_immediate_flush) {
_end_transfer_worker(transfer_worker);
_submit_transfer_worker(transfer_worker);
_wait_for_transfer_worker(transfer_worker);
}
_release_transfer_worker(transfer_worker);
}
}
@ -1863,6 +1885,17 @@ void RenderingDevice::_texture_create_reinterpret_buffer(Texture *p_texture) {
p_texture->shared_fallback->buffer_tracker = tracker;
}
uint32_t RenderingDevice::_texture_vrs_method_to_usage_bits() const {
switch (vrs_method) {
case VRS_METHOD_FRAGMENT_SHADING_RATE:
return RDD::TEXTURE_USAGE_VRS_FRAGMENT_SHADING_RATE_BIT;
case VRS_METHOD_FRAGMENT_DENSITY_MAP:
return RDD::TEXTURE_USAGE_VRS_FRAGMENT_DENSITY_MAP_BIT;
default:
return 0;
}
}
Vector<uint8_t> RenderingDevice::_texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d) {
uint32_t width, height, depth;
uint32_t tight_mip_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth);
@ -2424,7 +2457,7 @@ bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format,
/**** FRAMEBUFFER ****/
/*********************/
RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, uint32_t p_view_count, Vector<TextureSamples> *r_samples) {
RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, uint32_t p_view_count, VRSMethod p_vrs_method, int32_t p_vrs_attachment, Size2i p_vrs_texel_size, Vector<TextureSamples> *r_samples) {
// NOTE:
// Before the refactor to RenderingDevice-RenderingDeviceDriver, there was commented out code to
// specify dependencies to external subpasses. Since it had been unused for a long timel it wasn't ported
@ -2464,15 +2497,14 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_
// We can setup a framebuffer where we write to our VRS texture to set it up.
// We make the assumption here that if our texture is actually used as our VRS attachment.
// It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses.
bool is_vrs = (p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && i == p_passes[0].vrs_attachment;
bool is_vrs = (p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && i == p_vrs_attachment;
if (is_vrs) {
description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD;
description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE;
description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD;
description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE;
description.initial_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
description.final_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
description.initial_layout = _vrs_layout_from_method(p_vrs_method);
description.final_layout = _vrs_layout_from_method(p_vrs_method);
} else {
if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
description.load_op = p_load_ops[i];
@ -2605,14 +2637,15 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_
subpass.depth_stencil_reference.layout = RDD::TEXTURE_LAYOUT_UNDEFINED;
}
if (pass->vrs_attachment != ATTACHMENT_UNUSED) {
int32_t attachment = pass->vrs_attachment;
if (p_vrs_method == VRS_METHOD_FRAGMENT_SHADING_RATE && p_vrs_attachment >= 0) {
int32_t attachment = p_vrs_attachment;
ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), RDD::RenderPassID(), "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment.");
ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), RDD::RenderPassID(), "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment.");
ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, RDD::RenderPassID(), "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass.");
subpass.vrs_reference.attachment = attachment_remap[attachment];
subpass.vrs_reference.layout = RDD::TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL;
subpass.fragment_shading_rate_reference.attachment = attachment_remap[attachment];
subpass.fragment_shading_rate_reference.layout = RDD::TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL;
subpass.fragment_shading_rate_texel_size = p_vrs_texel_size;
attachment_last_pass[attachment] = i;
}
@ -2647,7 +2680,13 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_
}
}
RDD::RenderPassID render_pass = p_driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count);
RDD::AttachmentReference fragment_density_map_attachment_reference;
if (p_vrs_method == VRS_METHOD_FRAGMENT_DENSITY_MAP && p_vrs_attachment >= 0) {
fragment_density_map_attachment_reference.attachment = p_vrs_attachment;
fragment_density_map_attachment_reference.layout = RDD::TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL;
}
RDD::RenderPassID render_pass = p_driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count, fragment_density_map_attachment_reference);
ERR_FAIL_COND_V(!render_pass, RDD::RenderPassID());
return render_pass;
@ -2661,10 +2700,74 @@ RDD::RenderPassID RenderingDevice::_render_pass_create_from_graph(RenderingDevic
// resolving the dependencies between commands. This function creates a render pass for the framebuffer accordingly.
Framebuffer *framebuffer = (Framebuffer *)(p_user_data);
const FramebufferFormatKey &key = framebuffer->rendering_device->framebuffer_formats[framebuffer->format_id].E->key();
return _render_pass_create(p_driver, key.attachments, key.passes, p_load_ops, p_store_ops, framebuffer->view_count);
return _render_pass_create(p_driver, key.attachments, key.passes, p_load_ops, p_store_ops, framebuffer->view_count, key.vrs_method, key.vrs_attachment, key.vrs_texel_size);
}
RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) {
RDG::ResourceUsage RenderingDevice::_vrs_usage_from_method(VRSMethod p_method) {
switch (p_method) {
case VRS_METHOD_FRAGMENT_SHADING_RATE:
return RDG::RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ;
case VRS_METHOD_FRAGMENT_DENSITY_MAP:
return RDG::RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ;
default:
return RDG::RESOURCE_USAGE_NONE;
}
}
RDD::PipelineStageBits RenderingDevice::_vrs_stages_from_method(VRSMethod p_method) {
switch (p_method) {
case VRS_METHOD_FRAGMENT_SHADING_RATE:
return RDD::PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT;
case VRS_METHOD_FRAGMENT_DENSITY_MAP:
return RDD::PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT;
default:
return RDD::PipelineStageBits(0);
}
}
RDD::TextureLayout RenderingDevice::_vrs_layout_from_method(VRSMethod p_method) {
switch (p_method) {
case VRS_METHOD_FRAGMENT_SHADING_RATE:
return RDD::TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL;
case VRS_METHOD_FRAGMENT_DENSITY_MAP:
return RDD::TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL;
default:
return RDD::TEXTURE_LAYOUT_UNDEFINED;
}
}
void RenderingDevice::_vrs_detect_method() {
const RDD::FragmentShadingRateCapabilities &fsr_capabilities = driver->get_fragment_shading_rate_capabilities();
const RDD::FragmentDensityMapCapabilities &fdm_capabilities = driver->get_fragment_density_map_capabilities();
if (fsr_capabilities.attachment_supported) {
vrs_method = VRS_METHOD_FRAGMENT_SHADING_RATE;
} else if (fdm_capabilities.attachment_supported) {
vrs_method = VRS_METHOD_FRAGMENT_DENSITY_MAP;
}
switch (vrs_method) {
case VRS_METHOD_FRAGMENT_SHADING_RATE:
vrs_format = DATA_FORMAT_R8_UINT;
vrs_texel_size = Vector2i(16, 16).clamp(fsr_capabilities.min_texel_size, fsr_capabilities.max_texel_size);
break;
case VRS_METHOD_FRAGMENT_DENSITY_MAP:
vrs_format = DATA_FORMAT_R8G8_UNORM;
vrs_texel_size = Vector2i(32, 32).clamp(fdm_capabilities.min_texel_size, fdm_capabilities.max_texel_size);
break;
default:
break;
}
}
RD::DataFormat RenderingDevice::vrs_get_format() const {
return vrs_format;
}
Size2i RenderingDevice::vrs_get_texel_size() const {
return vrs_texel_size;
}
RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count, int32_t p_fragment_density_map_attachment) {
FramebufferPass pass;
for (int i = 0; i < p_format.size(); i++) {
if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
@ -2676,16 +2779,19 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(
Vector<FramebufferPass> passes;
passes.push_back(pass);
return framebuffer_format_create_multipass(p_format, passes, p_view_count);
return framebuffer_format_create_multipass(p_format, passes, p_view_count, p_fragment_density_map_attachment);
}
RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) {
RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count, int32_t p_vrs_attachment) {
_THREAD_SAFE_METHOD_
FramebufferFormatKey key;
key.attachments = p_attachments;
key.passes = p_passes;
key.view_count = p_view_count;
key.vrs_method = vrs_method;
key.vrs_attachment = p_vrs_attachment;
key.vrs_texel_size = vrs_texel_size;
const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key);
if (E) {
@ -2701,7 +2807,7 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_
store_ops.push_back(RDD::ATTACHMENT_STORE_OP_STORE);
}
RDD::RenderPassID render_pass = _render_pass_create(driver, p_attachments, p_passes, load_ops, store_ops, p_view_count, &samples); // Actions don't matter for this use case.
RDD::RenderPassID render_pass = _render_pass_create(driver, p_attachments, p_passes, load_ops, store_ops, p_view_count, vrs_method, p_vrs_attachment, vrs_texel_size, &samples); // Actions don't matter for this use case.
if (!render_pass) { // Was likely invalid.
return INVALID_ID;
}
@ -2741,7 +2847,7 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_
LocalVector<RDD::Subpass> subpass;
subpass.resize(1);
RDD::RenderPassID render_pass = driver->render_pass_create({}, subpass, {}, 1);
RDD::RenderPassID render_pass = driver->render_pass_create({}, subpass, {}, 1, RDD::AttachmentReference());
ERR_FAIL_COND_V(!render_pass, FramebufferFormatID());
FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT));
@ -2812,8 +2918,6 @@ RID RenderingDevice::framebuffer_create(const Vector<RID> &p_texture_attachments
if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
pass.depth_attachment = i;
} else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) {
pass.vrs_attachment = i;
} else {
if (texture && texture->is_resolve_buffer) {
pass.resolve_attachments.push_back(i);
@ -2835,6 +2939,7 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
Vector<AttachmentFormat> attachments;
LocalVector<RDD::TextureID> textures;
LocalVector<RDG::ResourceTracker *> trackers;
int32_t vrs_attachment = -1;
attachments.resize(p_texture_attachments.size());
Size2i size;
bool size_set = false;
@ -2849,6 +2954,11 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
_check_transfer_worker_texture(texture);
if (i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) {
// Detect if the texture is the fragment density map and it's not the first attachment.
vrs_attachment = i;
}
if (!size_set) {
size.width = texture->width;
size.height = texture->height;
@ -2876,7 +2986,7 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
ERR_FAIL_COND_V_MSG(!size_set, RID(), "All attachments unused.");
FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count);
FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count, vrs_attachment);
if (format_id == INVALID_ID) {
return RID();
}
@ -4259,7 +4369,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS
clear_value.color = p_clear_color;
RDD::RenderPassID render_pass = driver->swap_chain_get_render_pass(sc_it->value);
draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, RDG::ATTACHMENT_OPERATION_CLEAR, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS, split_swapchain_into_its_own_cmd_buffer);
draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, RDG::ATTACHMENT_OPERATION_CLEAR, clear_value, RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, RDD::BreadcrumbMarker::BLIT_PASS, split_swapchain_into_its_own_cmd_buffer);
draw_graph.add_draw_list_set_viewport(viewport);
draw_graph.add_draw_list_set_scissor(viewport);
@ -4275,6 +4385,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer);
ERR_FAIL_NULL_V(framebuffer, INVALID_ID);
const FramebufferFormatKey &framebuffer_key = framebuffer_formats[framebuffer->format_id].E->key();
Point2i viewport_offset;
Point2i viewport_size = framebuffer->size;
@ -4295,12 +4406,12 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
thread_local LocalVector<RDD::RenderPassClearValue> clear_values;
thread_local LocalVector<RDG::ResourceTracker *> resource_trackers;
thread_local LocalVector<RDG::ResourceUsage> resource_usages;
bool uses_color = false;
bool uses_depth = false;
BitField<RDD::PipelineStageBits> stages;
operations.resize(framebuffer->texture_ids.size());
clear_values.resize(framebuffer->texture_ids.size());
resource_trackers.clear();
resource_usages.clear();
stages.clear();
uint32_t color_index = 0;
for (int i = 0; i < framebuffer->texture_ids.size(); i++) {
@ -4317,7 +4428,11 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
RDG::AttachmentOperation operation = RDG::ATTACHMENT_OPERATION_DEFAULT;
RDD::RenderPassClearValue clear_value;
if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
if (framebuffer_key.vrs_attachment == i && (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) {
resource_trackers.push_back(texture->draw_tracker);
resource_usages.push_back(_vrs_usage_from_method(framebuffer_key.vrs_method));
stages.set_flag(_vrs_stages_from_method(framebuffer_key.vrs_method));
} else if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
if (p_draw_flags.has_flag(DrawFlags(DRAW_CLEAR_COLOR_0 << color_index))) {
ERR_FAIL_COND_V_MSG(color_index >= p_clear_color_values.size(), INVALID_ID, vformat("Color texture (%d) was specified to be cleared but no color value was provided.", color_index));
operation = RDG::ATTACHMENT_OPERATION_CLEAR;
@ -4328,7 +4443,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
resource_trackers.push_back(texture->draw_tracker);
resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE);
uses_color = true;
stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
color_index++;
} else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
if (p_draw_flags.has_flag(DRAW_CLEAR_DEPTH) || p_draw_flags.has_flag(DRAW_CLEAR_STENCIL)) {
@ -4341,14 +4456,15 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
resource_trackers.push_back(texture->draw_tracker);
resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE);
uses_depth = true;
stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
}
operations[i] = operation;
clear_values[i] = clear_value;
}
draw_graph.add_draw_list_begin(framebuffer->framebuffer_cache, Rect2i(viewport_offset, viewport_size), operations, clear_values, uses_color, uses_depth, p_breadcrumb);
draw_graph.add_draw_list_begin(framebuffer->framebuffer_cache, Rect2i(viewport_offset, viewport_size), operations, clear_values, stages, p_breadcrumb);
draw_graph.add_draw_list_usages(resource_trackers, resource_usages);
// Mark textures as bound.
@ -4369,9 +4485,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
draw_list_framebuffer_format = framebuffer->format_id;
#endif
draw_list_current_subpass = 0;
const FramebufferFormatKey &key = framebuffer_formats[framebuffer->format_id].E->key();
draw_list_subpass_count = key.passes.size();
draw_list_subpass_count = framebuffer_key.passes.size();
Rect2i viewport_rect(viewport_offset, viewport_size);
draw_graph.add_draw_list_set_viewport(viewport_rect);
@ -6832,6 +6946,9 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
}
}
// Find the best method available for VRS on the current hardware.
_vrs_detect_method();
return OK;
}
@ -7259,7 +7376,20 @@ RenderingDevice *RenderingDevice::create_local_device() {
}
bool RenderingDevice::has_feature(const Features p_feature) const {
return driver->has_feature(p_feature);
// Some features can be deduced from the capabilities without querying the driver and looking at the capabilities.
switch (p_feature) {
case SUPPORTS_MULTIVIEW: {
const RDD::MultiviewCapabilities &multiview_capabilities = driver->get_multiview_capabilities();
return multiview_capabilities.is_supported && multiview_capabilities.max_view_count > 1;
}
case SUPPORTS_ATTACHMENT_VRS: {
const RDD::FragmentShadingRateCapabilities &fsr_capabilities = driver->get_fragment_shading_rate_capabilities();
const RDD::FragmentDensityMapCapabilities &fdm_capabilities = driver->get_fragment_density_map_capabilities();
return fsr_capabilities.attachment_supported || fdm_capabilities.attachment_supported;
}
default:
return driver->has_feature(p_feature);
}
}
void RenderingDevice::_bind_methods() {

View File

@ -254,6 +254,8 @@ public:
CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE,
CALLBACK_RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE,
CALLBACK_RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE,
CALLBACK_RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ,
CALLBACK_RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ,
CALLBACK_RESOURCE_USAGE_MAX
};
@ -359,12 +361,13 @@ public:
Vector<uint8_t> _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false);
uint32_t _texture_layer_count(Texture *p_texture) const;
uint32_t _texture_alignment(Texture *p_texture) const;
Error _texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data);
Error _texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_immediate_flush = false);
void _texture_check_shared_fallback(Texture *p_texture);
void _texture_update_shared_fallback(RID p_texture_rid, Texture *p_texture, bool p_for_writing);
void _texture_free_shared_fallback(Texture *p_texture);
void _texture_copy_shared(RID p_src_texture_rid, Texture *p_src_texture, RID p_dst_texture_rid, Texture *p_dst_texture);
void _texture_create_reinterpret_buffer(Texture *p_texture);
uint32_t _texture_vrs_method_to_usage_bits() const;
struct TextureGetDataRequest {
uint32_t frame_local_index = 0;
@ -426,6 +429,30 @@ public:
void texture_set_discardable(RID p_texture, bool p_discardable);
bool texture_is_discardable(RID p_texture);
private:
/*************/
/**** VRS ****/
/*************/
enum VRSMethod {
VRS_METHOD_NONE,
VRS_METHOD_FRAGMENT_SHADING_RATE,
VRS_METHOD_FRAGMENT_DENSITY_MAP,
};
VRSMethod vrs_method = VRS_METHOD_NONE;
DataFormat vrs_format = DATA_FORMAT_MAX;
Size2i vrs_texel_size;
static RDG::ResourceUsage _vrs_usage_from_method(VRSMethod p_method);
static RDD::PipelineStageBits _vrs_stages_from_method(VRSMethod p_method);
static RDD::TextureLayout _vrs_layout_from_method(VRSMethod p_method);
void _vrs_detect_method();
public:
DataFormat vrs_get_format() const;
Size2i vrs_get_texel_size() const;
/*********************/
/**** FRAMEBUFFER ****/
/*********************/
@ -456,7 +483,6 @@ public:
Vector<int32_t> resolve_attachments;
Vector<int32_t> preserve_attachments;
int32_t depth_attachment = ATTACHMENT_UNUSED;
int32_t vrs_attachment = ATTACHMENT_UNUSED; // density map for VRS, only used if supported
};
typedef int64_t FramebufferFormatID;
@ -466,8 +492,23 @@ private:
Vector<AttachmentFormat> attachments;
Vector<FramebufferPass> passes;
uint32_t view_count = 1;
VRSMethod vrs_method = VRS_METHOD_NONE;
int32_t vrs_attachment = ATTACHMENT_UNUSED;
Size2i vrs_texel_size;
bool operator<(const FramebufferFormatKey &p_key) const {
if (vrs_texel_size != p_key.vrs_texel_size) {
return vrs_texel_size < p_key.vrs_texel_size;
}
if (vrs_attachment != p_key.vrs_attachment) {
return vrs_attachment < p_key.vrs_attachment;
}
if (vrs_method != p_key.vrs_method) {
return vrs_method < p_key.vrs_method;
}
if (view_count != p_key.view_count) {
return view_count < p_key.view_count;
}
@ -572,7 +613,7 @@ private:
}
};
static RDD::RenderPassID _render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, uint32_t p_view_count = 1, Vector<TextureSamples> *r_samples = nullptr);
static RDD::RenderPassID _render_pass_create(RenderingDeviceDriver *p_driver, const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, uint32_t p_view_count = 1, VRSMethod p_vrs_method = VRS_METHOD_NONE, int32_t p_vrs_attachment = -1, Size2i p_vrs_texel_size = Size2i(), Vector<TextureSamples> *r_samples = nullptr);
static RDD::RenderPassID _render_pass_create_from_graph(RenderingDeviceDriver *p_driver, VectorView<RDD::AttachmentLoadOp> p_load_ops, VectorView<RDD::AttachmentStoreOp> p_store_ops, void *p_user_data);
// This is a cache and it's never freed, it ensures
@ -603,8 +644,8 @@ private:
public:
// This ID is warranted to be unique for the same formats, does not need to be freed
FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1);
FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1);
FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1, int32_t p_vrs_attachment = -1);
FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1, int32_t p_vrs_attachment = -1);
FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1);
TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0);

View File

@ -391,6 +391,7 @@ public:
// Try to set this bit as much as possible. If you set it, validation doesn't complain
// and it works fine on mobile, then go ahead.
TEXTURE_USAGE_TRANSIENT_BIT = (1 << 11),
TEXTURE_USAGE_MAX_BIT = TEXTURE_USAGE_TRANSIENT_BIT,
};
struct TextureFormat {
@ -883,11 +884,7 @@ public:
LIMIT_SUBGROUP_MAX_SIZE,
LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc.
LIMIT_SUBGROUP_OPERATIONS,
LIMIT_VRS_TEXEL_WIDTH,
LIMIT_VRS_TEXEL_HEIGHT,
LIMIT_VRS_MAX_FRAGMENT_WIDTH,
LIMIT_VRS_MAX_FRAGMENT_HEIGHT,
LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE,
LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE = 46,
LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE,
LIMIT_MAX_SHADER_VARYINGS,
};

View File

@ -238,7 +238,8 @@ public:
TEXTURE_LAYOUT_COPY_DST_OPTIMAL,
TEXTURE_LAYOUT_RESOLVE_SRC_OPTIMAL,
TEXTURE_LAYOUT_RESOLVE_DST_OPTIMAL,
TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL,
TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL,
TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL,
TEXTURE_LAYOUT_MAX
};
@ -249,6 +250,11 @@ public:
TEXTURE_ASPECT_MAX
};
enum TextureUsageMethod {
TEXTURE_USAGE_VRS_FRAGMENT_SHADING_RATE_BIT = TEXTURE_USAGE_MAX_BIT << 1,
TEXTURE_USAGE_VRS_FRAGMENT_DENSITY_MAP_BIT = TEXTURE_USAGE_MAX_BIT << 2,
};
enum TextureAspectBits {
TEXTURE_ASPECT_COLOR_BIT = (1 << TEXTURE_ASPECT_COLOR),
TEXTURE_ASPECT_DEPTH_BIT = (1 << TEXTURE_ASPECT_DEPTH),
@ -335,6 +341,8 @@ public:
PIPELINE_STAGE_ALL_GRAPHICS_BIT = (1 << 15),
PIPELINE_STAGE_ALL_COMMANDS_BIT = (1 << 16),
PIPELINE_STAGE_CLEAR_STORAGE_BIT = (1 << 17),
PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT = (1 << 22),
PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT = (1 << 23),
};
enum BarrierAccessBits {
@ -356,8 +364,9 @@ public:
BARRIER_ACCESS_MEMORY_READ_BIT = (1 << 15),
BARRIER_ACCESS_MEMORY_WRITE_BIT = (1 << 16),
BARRIER_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT = (1 << 23),
BARRIER_ACCESS_RESOLVE_READ_BIT = (1 << 24),
BARRIER_ACCESS_RESOLVE_WRITE_BIT = (1 << 25),
BARRIER_ACCESS_FRAGMENT_DENSITY_MAP_ATTACHMENT_READ_BIT = (1 << 24),
BARRIER_ACCESS_RESOLVE_READ_BIT = (1 << 25),
BARRIER_ACCESS_RESOLVE_WRITE_BIT = (1 << 26),
BARRIER_ACCESS_STORAGE_CLEAR_BIT = (1 << 27),
};
@ -629,7 +638,8 @@ public:
AttachmentReference depth_stencil_reference;
LocalVector<AttachmentReference> resolve_references;
LocalVector<uint32_t> preserve_attachments;
AttachmentReference vrs_reference;
AttachmentReference fragment_shading_rate_reference;
Size2i fragment_shading_rate_texel_size;
};
struct SubpassDependency {
@ -641,7 +651,7 @@ public:
BitField<BarrierAccessBits> dst_access;
};
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) = 0;
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count, AttachmentReference p_fragment_density_map_attachment) = 0;
virtual void render_pass_free(RenderPassID p_render_pass) = 0;
// ----- COMMANDS -----
@ -787,6 +797,26 @@ public:
uint32_t max_instance_count = 0;
};
struct FragmentShadingRateCapabilities {
Size2i min_texel_size;
Size2i max_texel_size;
Size2i max_fragment_size;
bool pipeline_supported = false;
bool primitive_supported = false;
bool attachment_supported = false;
};
struct FragmentDensityMapCapabilities {
Size2i min_texel_size;
Size2i max_texel_size;
Size2i offset_granularity;
bool attachment_supported = false;
bool dynamic_attachment_supported = false;
bool non_subsampled_images_supported = false;
bool invocations_supported = false;
bool offset_supported = false;
};
enum ApiTrait {
API_TRAIT_HONORS_PIPELINE_BARRIERS,
API_TRAIT_SHADER_CHANGE_INVALIDATION,
@ -828,6 +858,8 @@ public:
virtual uint64_t api_trait_get(ApiTrait p_trait);
virtual bool has_feature(Features p_feature) = 0;
virtual const MultiviewCapabilities &get_multiview_capabilities() = 0;
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() = 0;
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() = 0;
virtual String get_api_name() const = 0;
virtual String get_api_version() const = 0;
virtual String get_pipeline_cache_uuid() const = 0;

View File

@ -98,6 +98,8 @@ bool RenderingDeviceGraph::_is_write_usage(ResourceUsage p_usage) {
case RESOURCE_USAGE_INDEX_BUFFER_READ:
case RESOURCE_USAGE_TEXTURE_SAMPLE:
case RESOURCE_USAGE_STORAGE_IMAGE_READ:
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ:
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ:
return false;
case RESOURCE_USAGE_COPY_TO:
case RESOURCE_USAGE_RESOLVE_TO:
@ -132,6 +134,10 @@ RDD::TextureLayout RenderingDeviceGraph::_usage_to_image_layout(ResourceUsage p_
return RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE:
return RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ:
return RDD::TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL;
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ:
return RDD::TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL;
case RESOURCE_USAGE_NONE:
return RDD::TEXTURE_LAYOUT_UNDEFINED;
default:
@ -176,6 +182,10 @@ RDD::BarrierAccessBits RenderingDeviceGraph::_usage_to_access_bits(ResourceUsage
return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE:
return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ:
return RDD::BARRIER_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT;
case RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ:
return RDD::BARRIER_ACCESS_FRAGMENT_DENSITY_MAP_ATTACHMENT_READ_BIT;
default:
DEV_ASSERT(false && "Invalid usage.");
return RDD::BarrierAccessBits(0);
@ -918,7 +928,7 @@ void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command
}
}
void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
DEV_ASSERT(p_attachment_operations.size() == p_attachment_clear_values.size());
draw_instruction_list.clear();
@ -927,6 +937,7 @@ void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_
draw_instruction_list.render_pass = p_render_pass;
draw_instruction_list.framebuffer = p_framebuffer;
draw_instruction_list.region = p_region;
draw_instruction_list.stages = p_stages;
draw_instruction_list.attachment_operations.resize(p_attachment_operations.size());
draw_instruction_list.attachment_clear_values.resize(p_attachment_clear_values.size());
@ -935,15 +946,6 @@ void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_
draw_instruction_list.attachment_clear_values[i] = p_attachment_clear_values[i];
}
if (p_uses_color) {
draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
}
if (p_uses_depth) {
draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
}
draw_instruction_list.split_cmd_buffer = p_split_cmd_buffer;
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
@ -1789,12 +1791,12 @@ void RenderingDeviceGraph::add_compute_list_end() {
_add_command_to_graph(compute_instruction_list.command_trackers.ptr(), compute_instruction_list.command_tracker_usages.ptr(), compute_instruction_list.command_trackers.size(), command_index, command);
}
void RenderingDeviceGraph::add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
_add_draw_list_begin(p_framebuffer_cache, RDD::RenderPassID(), RDD::FramebufferID(), p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb, p_split_cmd_buffer);
void RenderingDeviceGraph::add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
_add_draw_list_begin(p_framebuffer_cache, RDD::RenderPassID(), RDD::FramebufferID(), p_region, p_attachment_operations, p_attachment_clear_values, p_stages, p_breadcrumb, p_split_cmd_buffer);
}
void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
_add_draw_list_begin(nullptr, p_render_pass, p_framebuffer, p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb, p_split_cmd_buffer);
void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb, bool p_split_cmd_buffer) {
_add_draw_list_begin(nullptr, p_render_pass, p_framebuffer, p_region, p_attachment_operations, p_attachment_clear_values, p_stages, p_breadcrumb, p_split_cmd_buffer);
}
void RenderingDeviceGraph::add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset) {

View File

@ -149,6 +149,8 @@ public:
RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE,
RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE,
RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE,
RESOURCE_USAGE_ATTACHMENT_FRAGMENT_SHADING_RATE_READ,
RESOURCE_USAGE_ATTACHMENT_FRAGMENT_DENSITY_MAP_READ,
RESOURCE_USAGE_MAX
};
@ -752,7 +754,7 @@ private:
void _run_compute_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size);
void _get_draw_list_render_pass_and_framebuffer(const RecordedDrawListCommand *p_draw_list_command, RDD::RenderPassID &r_render_pass, RDD::FramebufferID &r_framebuffer);
void _run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size);
void _add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb, bool p_split_cmd_buffer);
void _add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb, bool p_split_cmd_buffer);
void _run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary);
void _wait_for_secondary_command_buffer_tasks();
void _run_render_commands(int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool, int32_t &r_current_label_index, int32_t &r_current_label_level);
@ -785,8 +787,8 @@ public:
void add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage);
void add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages);
void add_compute_list_end();
void add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0, bool p_split_cmd_buffer = false);
void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0, bool p_split_cmd_buffer = false);
void add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb = 0, bool p_split_cmd_buffer = false);
void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<AttachmentOperation> p_attachment_operations, VectorView<RDD::RenderPassClearValue> p_attachment_clear_values, BitField<RDD::PipelineStageBits> p_stages, uint32_t p_breadcrumb = 0, bool p_split_cmd_buffer = false);
void add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset);
void add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField<RDD::PipelineStageBits> p_pipeline_stage_bits);
void add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index);

View File

@ -104,13 +104,12 @@ void XRVRS::set_vrs_render_region(const Rect2i &p_vrs_render_region) {
RID XRVRS::make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array &p_eye_foci) {
ERR_FAIL_COND_V(p_eye_foci.is_empty(), RID());
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);
Size2i texel_size = RD::get_singleton()->vrs_get_texel_size();
// Should return sensible data or graphics API does not support VRS.
ERR_FAIL_COND_V(texel_width < 1 || texel_height < 1, RID());
ERR_FAIL_COND_V(texel_size.x < 1 || texel_size.y < 1, RID());
Size2 vrs_size = Size2(0.5 + p_target_size.x / texel_width, 0.5 + p_target_size.y / texel_height).round();
Size2 vrs_size = Size2(0.5 + p_target_size.x / texel_size.x, 0.5 + p_target_size.y / texel_size.y).floor();
// Make sure we have at least one pixel.
vrs_size = vrs_size.maxf(1.0);
@ -150,16 +149,18 @@ RID XRVRS::make_vrs_texture(const Size2 &p_target_size, const PackedVector2Array
Vector2i view_center;
view_center.x = int(vrs_size.x * (eye_foci[i].x + 1.0) * region_ratio.x * 0.5) + region_offset.x;
view_center.y = int(vrs_size.y * (eye_foci[i].y + 1.0) * region_ratio.y * 0.5) + region_offset.y;
view_center.y = int(vrs_size.y * (-eye_foci[i].y + 1.0) * region_ratio.y * 0.5) + region_offset.y;
int d = 0;
for (int y = 0; y < vrs_sizei.y; y++) {
for (int x = 0; x < vrs_sizei.x; x++) {
// Generate a density map that represents the distance to the view focus point. While this leaves the opportunities
// offered by the density map being different in each direction currently unused, it was found to give better tile
// distribution on hardware that supports the feature natively. This area is open to improvements in the future.
Vector2 offset = Vector2(x - view_center.x, y - view_center.y) / region_ratio;
real_t density = 255.0 * MAX(0.0, (Math::abs(offset.x) - min_radius) / outer_radius);
data_ptr[d++] = MIN(255, density);
density = 255.0 * MAX(0.0, (Math::abs(offset.y) - min_radius) / outer_radius);
data_ptr[d++] = MIN(255, density);
real_t density = MAX(offset.length() - min_radius, 0.0) / outer_radius;
data_ptr[d++] = CLAMP(255.0 * density, 0, 255);
data_ptr[d++] = CLAMP(255.0 * density, 0, 255);
}
}
images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_RG8, data));