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

@ -1222,7 +1222,7 @@ RDD::TextureID RenderingDeviceDriverD3D12::texture_create(const TextureFormat &p
if ((p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) {
resource_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
if ((p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) {
if ((p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && (p_format.usage_bits & TEXTURE_USAGE_VRS_FRAGMENT_SHADING_RATE_BIT)) {
// For VRS images we can't use the typeless format.
resource_desc.Format = DXGI_FORMAT_R8_UINT;
}
@ -1827,8 +1827,11 @@ static D3D12_BARRIER_ACCESS _rd_texture_layout_access_mask(RDD::TextureLayout p_
return D3D12_BARRIER_ACCESS_RESOLVE_SOURCE;
case RDD::TEXTURE_LAYOUT_RESOLVE_DST_OPTIMAL:
return D3D12_BARRIER_ACCESS_RESOLVE_DEST;
case RDD::TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL:
case RDD::TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL:
return D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE;
case RDD::TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL:
DEV_ASSERT(false && "Fragment density maps are not supported in D3D12.");
return D3D12_BARRIER_ACCESS_NO_ACCESS;
default:
return D3D12_BARRIER_ACCESS_NO_ACCESS;
}
@ -1947,7 +1950,7 @@ static void _rd_stages_to_d3d12(BitField<RDD::PipelineStageBits> p_stages, D3D12
r_sync |= D3D12_BARRIER_SYNC_VERTEX_SHADING;
}
if (p_stages.has_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT)) {
if (p_stages.has_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT) || p_stages.has_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT)) {
r_sync |= D3D12_BARRIER_SYNC_PIXEL_SHADING;
}
@ -2042,8 +2045,11 @@ static D3D12_BARRIER_LAYOUT _rd_texture_layout_to_d3d12_barrier_layout(RDD::Text
return D3D12_BARRIER_LAYOUT_RESOLVE_SOURCE;
case RDD::TEXTURE_LAYOUT_RESOLVE_DST_OPTIMAL:
return D3D12_BARRIER_LAYOUT_RESOLVE_DEST;
case RDD::TEXTURE_LAYOUT_VRS_ATTACHMENT_OPTIMAL:
case RDD::TEXTURE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL:
return D3D12_BARRIER_LAYOUT_SHADING_RATE_SOURCE;
case RDD::TEXTURE_LAYOUT_FRAGMENT_DENSITY_MAP_ATTACHMENT_OPTIMAL:
DEV_ASSERT(false && "Fragment density maps are not supported in D3D12.");
return D3D12_BARRIER_LAYOUT_UNDEFINED;
default:
DEV_ASSERT(false && "Unknown texture layout.");
return D3D12_BARRIER_LAYOUT_UNDEFINED;
@ -2451,7 +2457,7 @@ RDD::SwapChainID RenderingDeviceDriverD3D12::swap_chain_create(RenderingContextD
color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT);
subpass.color_references.push_back(color_ref);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1, AttachmentReference());
ERR_FAIL_COND_V(!render_pass, SwapChainID());
// Create the empty swap chain until it is resized.
@ -2811,8 +2817,8 @@ RDD::FramebufferID RenderingDeviceDriverD3D12::_framebuffer_create(RenderPassID
uint32_t vrs_index = UINT32_MAX;
for (const Subpass &E : pass_info->subpasses) {
if (E.vrs_reference.attachment != AttachmentReference::UNUSED) {
vrs_index = E.vrs_reference.attachment;
if (E.fragment_shading_rate_reference.attachment != AttachmentReference::UNUSED) {
vrs_index = E.fragment_shading_rate_reference.attachment;
}
}
@ -4963,7 +4969,9 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::pipeline_cache_serialize() {
// ----- SUBPASS -----
RDD::RenderPassID RenderingDeviceDriverD3D12::render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) {
RDD::RenderPassID RenderingDeviceDriverD3D12::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) {
ERR_FAIL_COND_V_MSG(p_fragment_density_map_attachment.attachment != AttachmentReference::UNUSED, RenderPassID(), "Fragment density maps are not supported in D3D12.");
// Pre-bookkeep.
RenderPassInfo *pass_info = VersatileResource::allocate<RenderPassInfo>(resources_allocator);
@ -5064,7 +5072,7 @@ void RenderingDeviceDriverD3D12::command_begin_render_pass(CommandBufferID p_cmd
}
}
if (fb_info->vrs_attachment && vrs_capabilities.ss_image_supported) {
if (fb_info->vrs_attachment && fsr_capabilities.attachment_supported) {
ComPtr<ID3D12GraphicsCommandList5> cmd_list_5;
cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf());
if (cmd_list_5) {
@ -5184,7 +5192,7 @@ void RenderingDeviceDriverD3D12::command_end_render_pass(CommandBufferID p_cmd_b
const FramebufferInfo *fb_info = cmd_buf_info->render_pass_state.fb_info;
const RenderPassInfo *pass_info = cmd_buf_info->render_pass_state.pass_info;
if (vrs_capabilities.ss_image_supported) {
if (fsr_capabilities.attachment_supported) {
ComPtr<ID3D12GraphicsCommandList5> cmd_list_5;
cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf());
if (cmd_list_5) {
@ -6244,12 +6252,6 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
return subgroup_capabilities.supported_stages_flags_rd();
case LIMIT_SUBGROUP_OPERATIONS:
return subgroup_capabilities.supported_operations_flags_rd();
case LIMIT_VRS_TEXEL_WIDTH:
case LIMIT_VRS_TEXEL_HEIGHT:
return vrs_capabilities.ss_image_tile_size;
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
return vrs_capabilities.ss_max_fragment_size;
case LIMIT_MAX_SHADER_VARYINGS:
return MIN(D3D12_VS_OUTPUT_REGISTER_COUNT, D3D12_PS_INPUT_REGISTER_COUNT);
default: {
@ -6286,12 +6288,8 @@ uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) {
bool RenderingDeviceDriverD3D12::has_feature(Features p_feature) {
switch (p_feature) {
case SUPPORTS_MULTIVIEW:
return multiview_capabilities.is_supported && multiview_capabilities.max_view_count > 1;
case SUPPORTS_FSR_HALF_FLOAT:
return shader_capabilities.native_16bit_ops && storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported;
case SUPPORTS_ATTACHMENT_VRS:
return vrs_capabilities.ss_image_supported;
case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS:
return true;
case SUPPORTS_BUFFER_DEVICE_ADDRESS:
@ -6305,6 +6303,14 @@ const RDD::MultiviewCapabilities &RenderingDeviceDriverD3D12::get_multiview_capa
return multiview_capabilities;
}
const RDD::FragmentShadingRateCapabilities &RenderingDeviceDriverD3D12::get_fragment_shading_rate_capabilities() {
return fsr_capabilities;
}
const RDD::FragmentDensityMapCapabilities &RenderingDeviceDriverD3D12::get_fragment_density_map_capabilities() {
return fdm_capabilities;
}
String RenderingDeviceDriverD3D12::get_api_name() const {
return "D3D12";
}
@ -6466,12 +6472,6 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
device_capabilities.version_minor = feature_level % 10;
// Assume not supported until proven otherwise.
vrs_capabilities.draw_call_supported = false;
vrs_capabilities.primitive_supported = false;
vrs_capabilities.primitive_in_multiviewport = false;
vrs_capabilities.ss_image_supported = false;
vrs_capabilities.ss_image_tile_size = 1;
vrs_capabilities.additional_rates_supported = false;
multiview_capabilities.is_supported = false;
multiview_capabilities.geometry_shader_is_supported = false;
multiview_capabilities.tessellation_shader_is_supported = false;
@ -6562,14 +6562,12 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, sizeof(options6));
if (SUCCEEDED(res)) {
if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_1) {
vrs_capabilities.draw_call_supported = true;
fsr_capabilities.pipeline_supported = true;
if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_2) {
vrs_capabilities.primitive_supported = true;
vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing;
vrs_capabilities.ss_image_supported = true;
vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize;
vrs_capabilities.ss_max_fragment_size = 8; // TODO figure out if this is supplied and/or needed
vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported;
fsr_capabilities.primitive_supported = true;
fsr_capabilities.attachment_supported = true;
fsr_capabilities.min_texel_size = Size2i(options6.ShadingRateImageTileSize, options6.ShadingRateImageTileSize);
fsr_capabilities.max_texel_size = Size2i(8, 8);
}
}
}
@ -6581,19 +6579,16 @@ Error RenderingDeviceDriverD3D12::_check_capabilities() {
barrier_capabilities.enhanced_barriers_supported = options12.EnhancedBarriersSupported;
}
if (vrs_capabilities.draw_call_supported || vrs_capabilities.primitive_supported || vrs_capabilities.ss_image_supported) {
if (fsr_capabilities.pipeline_supported || fsr_capabilities.primitive_supported || fsr_capabilities.attachment_supported) {
print_verbose("- D3D12 Variable Rate Shading supported:");
if (vrs_capabilities.draw_call_supported) {
if (fsr_capabilities.pipeline_supported) {
print_verbose(" Draw call");
}
if (vrs_capabilities.primitive_supported) {
print_verbose(String(" Per-primitive (multi-viewport: ") + (vrs_capabilities.primitive_in_multiviewport ? "yes" : "no") + ")");
if (fsr_capabilities.primitive_supported) {
print_verbose(" Primitive");
}
if (vrs_capabilities.ss_image_supported) {
print_verbose(String(" Screen-space image (tile size: ") + itos(vrs_capabilities.ss_image_tile_size) + ")");
}
if (vrs_capabilities.additional_rates_supported) {
print_verbose(String(" Additional rates: ") + (vrs_capabilities.additional_rates_supported ? "yes" : "no"));
if (fsr_capabilities.attachment_supported) {
print_verbose(String(" Screen-space image (tile size: ") + itos(fsr_capabilities.min_texel_size.x) + ")");
}
} else {
print_verbose("- D3D12 Variable Rate Shading not supported");

View File

@ -116,16 +116,6 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
uint32_t supported_operations_flags_rd() const;
};
struct VRSCapabilities {
bool draw_call_supported = false; // We can specify our fragment rate on a draw call level.
bool primitive_supported = false; // We can specify our fragment rate on each drawcall.
bool primitive_in_multiviewport = false;
bool ss_image_supported = false; // We can provide a density map attachment on our framebuffer.
uint32_t ss_image_tile_size = 0;
uint32_t ss_max_fragment_size = 0;
bool additional_rates_supported = false;
};
struct ShaderCapabilities {
D3D_SHADER_MODEL shader_model = (D3D_SHADER_MODEL)0;
bool native_16bit_ops = false;
@ -157,7 +147,8 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
uint32_t feature_level = 0; // Major * 10 + minor.
SubgroupCapabilities subgroup_capabilities;
RDD::MultiviewCapabilities multiview_capabilities;
VRSCapabilities vrs_capabilities;
FragmentShadingRateCapabilities fsr_capabilities;
FragmentDensityMapCapabilities fdm_capabilities;
ShaderCapabilities shader_capabilities;
StorageBufferCapabilities storage_buffer_capabilities;
FormatCapabilities format_capabilities;
@ -834,7 +825,7 @@ private:
};
public:
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
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) override final;
virtual void render_pass_free(RenderPassID p_render_pass) override final;
// ----- COMMANDS -----
@ -1002,6 +993,8 @@ public:
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
virtual String get_api_name() const override final;
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;

View File

@ -66,6 +66,8 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
RDD::Capabilities capabilities;
RDD::MultiviewCapabilities multiview_capabilities;
RDD::FragmentShadingRateCapabilities fsr_capabilities;
RDD::FragmentDensityMapCapabilities fdm_capabilities;
id<MTLBinaryArchive> archive = nil;
uint32_t archive_count = 0;
@ -316,7 +318,7 @@ public:
// ----- SUBPASS -----
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
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) override final;
virtual void render_pass_free(RenderPassID p_render_pass) override final;
// ----- COMMANDS -----
@ -421,6 +423,8 @@ public:
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
virtual String get_api_name() const override final { return "Metal"; }
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;

View File

@ -974,7 +974,7 @@ RDD::SwapChainID RenderingDeviceDriverMetal::swap_chain_create(RenderingContextD
color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT);
subpass.color_references.push_back(color_ref);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1, RDD::AttachmentReference());
ERR_FAIL_COND_V(!render_pass, SwapChainID());
// Create the empty swap chain until it is resized.
@ -3104,7 +3104,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::pipeline_cache_serialize() {
// ----- SUBPASS -----
RDD::RenderPassID RenderingDeviceDriverMetal::render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) {
RDD::RenderPassID RenderingDeviceDriverMetal::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) {
PixelFormats &pf = *pixel_formats;
size_t subpass_count = p_subpasses.size();
@ -4006,10 +4006,6 @@ uint64_t RenderingDeviceDriverMetal::limit_get(Limit p_limit) {
return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000);
case LIMIT_MAX_SHADER_VARYINGS:
return limits.maxShaderVaryings;
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_HEIGHT);
default: {
#ifdef DEV_ENABLED
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");
@ -4032,17 +4028,8 @@ uint64_t RenderingDeviceDriverMetal::api_trait_get(ApiTrait p_trait) {
bool RenderingDeviceDriverMetal::has_feature(Features p_feature) {
switch (p_feature) {
case SUPPORTS_MULTIVIEW:
return multiview_capabilities.is_supported;
case SUPPORTS_FSR_HALF_FLOAT:
return true;
case SUPPORTS_ATTACHMENT_VRS:
// TODO(sgc): Maybe supported via https://developer.apple.com/documentation/metal/render_passes/rendering_at_different_rasterization_rates?language=objc
// See also:
//
// * https://forum.beyond3d.com/threads/variable-rate-shading-vs-variable-rate-rasterization.62243/post-2191363
//
return false;
case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS:
return true;
case SUPPORTS_BUFFER_DEVICE_ADDRESS:
@ -4060,6 +4047,14 @@ const RDD::MultiviewCapabilities &RenderingDeviceDriverMetal::get_multiview_capa
return multiview_capabilities;
}
const RDD::FragmentShadingRateCapabilities &RenderingDeviceDriverMetal::get_fragment_shading_rate_capabilities() {
return fsr_capabilities;
}
const RDD::FragmentDensityMapCapabilities &RenderingDeviceDriverMetal::get_fragment_density_map_capabilities() {
return fdm_capabilities;
}
String RenderingDeviceDriverMetal::get_api_version() const {
return vformat("%d.%d", version_major, version_minor);
}

File diff suppressed because it is too large Load Diff

View File

@ -54,6 +54,9 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
struct CommandQueue;
struct SwapChain;
struct CommandBufferInfo;
struct RenderPassInfo;
struct Framebuffer;
struct Queue {
VkQueue queue = VK_NULL_HANDLE;
@ -76,18 +79,6 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
String supported_operations_desc() const;
};
struct VRSCapabilities {
bool pipeline_vrs_supported = false; // We can specify our fragment rate on a pipeline level.
bool primitive_vrs_supported = false; // We can specify our fragment rate on each drawcall.
bool attachment_vrs_supported = false; // We can provide a density map attachment on our framebuffer.
Size2i min_texel_size;
Size2i max_texel_size;
Size2i max_fragment_size;
Size2i texel_size; // The texel size we'll use
};
struct ShaderCapabilities {
bool shader_float16_is_supported = false;
bool shader_int8_is_supported = false;
@ -107,6 +98,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
PFN_vkAcquireNextImageKHR AcquireNextImageKHR = nullptr;
PFN_vkQueuePresentKHR QueuePresentKHR = nullptr;
PFN_vkCreateRenderPass2KHR CreateRenderPass2KHR = nullptr;
PFN_vkCmdEndRenderPass2KHR EndRenderPass2KHR = nullptr;
// Debug marker extensions.
PFN_vkCmdDebugMarkerBeginEXT CmdDebugMarkerBeginEXT = nullptr;
@ -135,7 +127,8 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
RDD::Capabilities device_capabilities;
SubgroupCapabilities subgroup_capabilities;
MultiviewCapabilities multiview_capabilities;
VRSCapabilities vrs_capabilities;
FragmentShadingRateCapabilities fsr_capabilities;
FragmentDensityMapCapabilities fdm_capabilities;
ShaderCapabilities shader_capabilities;
StorageBufferCapabilities storage_buffer_capabilities;
bool buffer_device_address_support = false;
@ -155,6 +148,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
Error _initialize_device_extensions();
Error _check_device_features();
Error _check_device_capabilities();
void _choose_vrs_capabilities();
Error _add_queue_create_info(LocalVector<VkDeviceQueueCreateInfo> &r_queue_create_info);
Error _initialize_device(const LocalVector<VkDeviceQueueCreateInfo> &p_queue_create_info);
Error _initialize_allocator();
@ -332,6 +326,7 @@ private:
struct CommandPool {
VkCommandPool vk_command_pool = VK_NULL_HANDLE;
CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY;
LocalVector<CommandBufferInfo *> command_buffers_created;
};
public:
@ -339,8 +334,16 @@ public:
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
private:
// ----- BUFFER -----
struct CommandBufferInfo {
VkCommandBuffer vk_command_buffer = VK_NULL_HANDLE;
Framebuffer *active_framebuffer = nullptr;
RenderPassInfo *active_render_pass = nullptr;
};
public:
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
@ -382,6 +385,7 @@ public:
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
private:
/*********************/
/**** FRAMEBUFFER ****/
/*********************/
@ -389,12 +393,16 @@ public:
struct Framebuffer {
VkFramebuffer vk_framebuffer = VK_NULL_HANDLE;
// Only filled in if the framebuffer uses a fragment density map with offsets. Unused otherwise.
uint32_t fragment_density_map_offsets_layers = 0;
// Only filled in by a framebuffer created by a swap chain. Unused otherwise.
VkImage swap_chain_image = VK_NULL_HANDLE;
VkImageSubresourceRange swap_chain_image_subresource_range = {};
bool swap_chain_acquired = false;
};
public:
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
@ -572,9 +580,16 @@ public:
/**** RENDERING ****/
/*******************/
private:
// ----- SUBPASS -----
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
struct RenderPassInfo {
VkRenderPass vk_render_pass = VK_NULL_HANDLE;
bool uses_fragment_density_map_offsets = false;
};
public:
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) override final;
virtual void render_pass_free(RenderPassID p_render_pass) override final;
// ----- COMMANDS -----
@ -692,6 +707,8 @@ public:
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
virtual String get_api_name() const override final;
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;
@ -709,7 +726,9 @@ private:
TextureInfo,
VertexFormatInfo,
ShaderInfo,
UniformSetInfo>;
UniformSetInfo,
RenderPassInfo,
CommandBufferInfo>;
PagedAllocator<VersatileResource, true> resources_allocator;
/******************/

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));