renderer: support depth-compatible uint image views

This commit is contained in:
nmzik
2026-08-17 11:02:44 +02:00
parent ce39238210
commit c759236ccb
5 changed files with 108 additions and 53 deletions
@@ -212,7 +212,6 @@ struct DepthFormatPolicy {
Prospero::DepthFormat depth_format; Prospero::DepthFormat depth_format;
Prospero::BufferFormat guest_format; Prospero::BufferFormat guest_format;
uint32_t bytes_per_element; uint32_t bytes_per_element;
vk::Format sampled_view_format;
vk::Format depth_attachment_format; vk::Format depth_attachment_format;
std::array<vk::Format, 3> stencil_attachment_formats; std::array<vk::Format, 3> stencil_attachment_formats;
}; };
@@ -221,13 +220,11 @@ inline constexpr std::array<DepthFormatPolicy, 2> DEPTH_FORMAT_POLICIES {{
{Prospero::DepthFormat::kZ16, {Prospero::DepthFormat::kZ16,
Prospero::BufferFormat::k16UNorm, Prospero::BufferFormat::k16UNorm,
2, 2,
vk::Format::eR16Unorm,
vk::Format::eD16Unorm, vk::Format::eD16Unorm,
{vk::Format::eD16UnormS8Uint, vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint}}, {vk::Format::eD16UnormS8Uint, vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint}},
{Prospero::DepthFormat::kZ32F, {Prospero::DepthFormat::kZ32F,
Prospero::BufferFormat::k32Float, Prospero::BufferFormat::k32Float,
4, 4,
vk::Format::eR32Sfloat,
vk::Format::eD32Sfloat, vk::Format::eD32Sfloat,
{vk::Format::eD32SfloatS8Uint, vk::Format::eUndefined, vk::Format::eUndefined}}, {vk::Format::eD32SfloatS8Uint, vk::Format::eUndefined, vk::Format::eUndefined}},
}}; }};
@@ -317,25 +314,6 @@ inline bool ImageInfo::IsDepth() const noexcept {
return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f); return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f);
} }
[[nodiscard]] inline constexpr bool
IsSupportedSampledDepthFormat(vk::Format image_format, Prospero::BufferFormat guest_format,
vk::Format view_format) noexcept {
const auto* policy = FindGuestDepthFormatPolicy(guest_format);
return policy != nullptr && view_format == policy->sampled_view_format &&
(image_format == policy->depth_attachment_format ||
IsStencilAttachmentFormat(*policy, image_format));
}
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
vk::Format view_format) noexcept {
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
if (IsSupportedSampledDepthFormat(image_format, policy.guest_format, view_format)) {
return true;
}
}
return false;
}
[[nodiscard]] inline constexpr bool IsSupportedDepthTargetFormat(const ImageInfo& info) { [[nodiscard]] inline constexpr bool IsSupportedDepthTargetFormat(const ImageInfo& info) {
const auto* policy = FindGuestDepthFormatPolicy(info.guest_format); const auto* policy = FindGuestDepthFormatPolicy(info.guest_format);
return policy != nullptr && info.bytes_per_block == policy->bytes_per_element && return policy != nullptr && info.bytes_per_block == policy->bytes_per_element &&
@@ -36,17 +36,6 @@ namespace {
} }
} }
[[nodiscard]] bool IsDepthViewFormat(vk::Format format) {
switch (format) {
case vk::Format::eD16Unorm:
case vk::Format::eR16Unorm:
case vk::Format::eD32Sfloat:
case vk::Format::eR32Sfloat:
case vk::Format::eR32Uint: return true;
default: return false;
}
}
[[nodiscard]] bool IsValidViewType(const VulkanImage& image, const ImageViewInfo& info) { [[nodiscard]] bool IsValidViewType(const VulkanImage& image, const ImageViewInfo& info) {
switch (image.image_type) { switch (image.image_type) {
case vk::ImageType::e1D: case vk::ImageType::e1D:
@@ -328,7 +317,8 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
auto normalized = view_info; auto normalized = view_info;
const bool is_storage = static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage); const bool is_storage = static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage);
const auto image_aspect = FullAspectMask(image.format); const auto image_aspect = FullAspectMask(image.format);
if (image_aspect & vk::ImageAspectFlagBits::eDepth && IsDepthViewFormat(normalized.format)) { if (image_aspect & vk::ImageAspectFlagBits::eDepth &&
ImageViewOps::IsFormatDepthCompatible(normalized.format)) {
normalized.format = image.format; normalized.format = image.format;
normalized.aspect = vk::ImageAspectFlagBits::eDepth; normalized.aspect = vk::ImageAspectFlagBits::eDepth;
} }
@@ -12,8 +12,25 @@ namespace ImageViewOps {
[[nodiscard]] vk::ImageAspectFlags DepthAspectMask(vk::Format format); [[nodiscard]] vk::ImageAspectFlags DepthAspectMask(vk::Format format);
[[nodiscard]] bool FormatsCompatible(vk::Format base, vk::Format view) noexcept; [[nodiscard]] bool FormatsCompatible(vk::Format base, vk::Format view) noexcept;
[[nodiscard]] inline bool IsFormatDepthCompatible(vk::Format format) noexcept {
switch (format) {
case vk::Format::eD32Sfloat:
case vk::Format::eR32Sfloat:
case vk::Format::eR32Uint:
case vk::Format::eD16Unorm:
case vk::Format::eR16Unorm: return true;
default: return false;
}
}
} // namespace ImageViewOps } // namespace ImageViewOps
[[nodiscard]] inline bool IsSupportedSampledDepthFormat(vk::Format image_format,
vk::Format view_format) noexcept {
return DepthAspectTransferFormat(image_format) != vk::Format::eUndefined &&
ImageViewOps::IsFormatDepthCompatible(view_format);
}
[[nodiscard]] inline bool IsValidImageSwizzle(uint32_t swizzle) noexcept { [[nodiscard]] inline bool IsValidImageSwizzle(uint32_t swizzle) noexcept {
if ((swizzle & ~0xfffu) != 0) { if ((swizzle & ~0xfffu) != 0) {
return false; return false;
@@ -86,8 +103,14 @@ SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t
[[nodiscard]] inline bool [[nodiscard]] inline bool
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
return resource.kind == ShaderRecompiler::IR::ResourceKind::Image && if (resource.kind != ShaderRecompiler::IR::ResourceKind::Image &&
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D || resource.kind != ShaderRecompiler::IR::ResourceKind::ImageUint) {
return false;
}
if (resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint && resource.depth_compare) {
return false;
}
return (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray || resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa || resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray) && resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray) &&
@@ -95,14 +118,6 @@ IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resou
!resource.written && !resource.atomic; !resource.written && !resource.atomic;
} }
[[nodiscard]] inline bool
IsSupportedSampledDepthUintResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
return resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint &&
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
!resource.written && !resource.atomic && !resource.depth_compare;
}
inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format, inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format,
uint32_t swizzle) noexcept { uint32_t swizzle) noexcept {
if (!ImageViewOps::FormatsCompatible(image_format, view_format) || if (!ImageViewOps::FormatsCompatible(image_format, view_format) ||
@@ -316,8 +316,7 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
const bool encoding_ok = const bool encoding_ok =
image != nullptr && IsSupportedDepthTextureEncoding(descriptor, *image); image != nullptr && IsSupportedDepthTextureEncoding(descriptor, *image);
const bool format_ok = const bool format_ok =
image != nullptr && image != nullptr && IsSupportedSampledDepthFormat(image->info.pixel_format, view_format);
IsSupportedSampledDepthFormat(image->info.pixel_format, descriptor.Format(), view_format);
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) { if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
return; return;
} }
+80 -7
View File
@@ -7344,6 +7344,71 @@ public:
"attachment"); "attachment");
RenderExecutorTestAccess::ResetBindings(executor); RenderExecutorTestAccess::ResetBindings(executor);
const auto sampled_width = depth_only.desc.info.extent.width - 1u;
const auto sampled_height = depth_only.desc.info.extent.height - 1u;
const auto sampled_address = depth_only_address >> 8u;
ShaderTextureResource sampled_depth_descriptor{};
sampled_depth_descriptor.fields[0] =
static_cast<uint32_t>(sampled_address);
sampled_depth_descriptor.fields[1] =
static_cast<uint32_t>(sampled_address >> 32u) |
(static_cast<uint32_t>(Prospero::BufferFormat::k32UInt) << 20u) |
((sampled_width & 3u) << 30u);
sampled_depth_descriptor.fields[2] =
(sampled_width >> 2u) | (sampled_height << 14u);
sampled_depth_descriptor.fields[3] =
DstSel(4, 4, 4, 4) |
(static_cast<uint32_t>(Prospero::TileMode::kDepth) << 20u) |
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u);
sampled_depth_descriptor.fields[5] = 0x00700000u;
ShaderRecompiler::IR::DescriptorValue sampled_depth_value{};
std::copy(std::begin(sampled_depth_descriptor.fields),
std::end(sampled_depth_descriptor.fields),
sampled_depth_value.dwords.begin());
sampled_depth_value.dword_count = 8;
ShaderRecompiler::IR::ImageResource sampled_depth_resource{};
sampled_depth_resource.kind =
ShaderRecompiler::IR::ResourceKind::ImageUint;
sampled_depth_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D;
sampled_depth_resource.read = true;
const auto sampled_depth_backing =
texture_cache.GetImage(depth_only.image_id).backing.image;
auto sampled_depth_binding = RenderExecutorTestAccess::ResolveTexture(
executor, sampled_depth_resource, sampled_depth_value);
const auto sampled_depth_view = texture_cache.FindTexture(
sampled_depth_binding.image_id, sampled_depth_binding.desc);
const auto &sampled_depth_image =
texture_cache.GetImage(sampled_depth_binding.image_id);
const bool normalized_depth_view = std::ranges::any_of(
sampled_depth_image.views.views, [&](const auto &cached) {
return cached.view == sampled_depth_view &&
cached.info.format == vk::Format::eD32Sfloat &&
cached.info.aspect == vk::ImageAspectFlagBits::eDepth &&
cached.info.usage == vk::ImageUsageFlags{};
});
Require(
name, "R32 uint depth-backed view",
sampled_depth_binding.image_id == depth_only.image_id &&
sampled_depth_binding.desc.info.guest_format ==
Prospero::BufferFormat::k32UInt &&
sampled_depth_binding.desc.info.pixel_format ==
vk::Format::eR32Uint &&
sampled_depth_binding.desc.view_info.format ==
vk::Format::eR32Uint &&
sampled_depth_binding.desc.view_info.aspect ==
vk::ImageAspectFlagBits::eColor &&
sampled_depth_binding.desc.view_info.usage ==
vk::ImageUsageFlagBits::eSampled &&
sampled_depth_image.info.pixel_format ==
vk::Format::eD32Sfloat &&
sampled_depth_image.backing.format == vk::Format::eD32Sfloat &&
sampled_depth_image.backing.image == sampled_depth_backing &&
sampled_depth_view != nullptr && normalized_depth_view,
"uint T# did not retain its requested format while the concrete "
"view followed the D32 backing");
RenderExecutorTestAccess::ResetBindings(executor);
auto video_subresource = auto video_subresource =
make_target_desc(base + 0x20000, target_mip_size, {1, 1, 1}); make_target_desc(base + 0x20000, target_mip_size, {1, 1, 1});
video_subresource.type = BindingType::VideoOut; video_subresource.type = BindingType::VideoOut;
@@ -16650,7 +16715,8 @@ TestCase ImageLoadR32UintUsesIntegerSampledImage() {
test.sampled_image_dwords_per_pixel = 1; test.sampled_image_dwords_per_pixel = 1;
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt); test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
test.has_user_data = true; test.has_user_data = true;
test.required_spirv = {"sampled_uint_2d"}; test.required_spirv = {"sampled_uint_2d", "OpTypeImage %uint",
"OpImageFetch %v4uint"};
return test; return test;
} }
@@ -18398,7 +18464,7 @@ void CheckRenderTargetFormatContract() {
DstSel(7, 6, 5, 3)); DstSel(7, 6, 5, 3));
} else if (std::strcmp(kind, "sampled-depth-format") == 0) { } else if (std::strcmp(kind, "sampled-depth-format") == 0) {
(void)SelectSampledDepthView(vk::Format::eD24UnormS8Uint, (void)SelectSampledDepthView(vk::Format::eD24UnormS8Uint,
vk::Format::eR32Sfloat, DstSel(4, 4, 4, 4)); vk::Format::eR8Unorm, DstSel(4, 4, 4, 4));
} else if (std::strcmp(kind, "sampled-depth-swizzle") == 0) { } else if (std::strcmp(kind, "sampled-depth-swizzle") == 0) {
(void)SelectSampledDepthView(vk::Format::eD32SfloatS8Uint, (void)SelectSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR32Sfloat, DstSel(4, 5, 6, 7)); vk::Format::eR32Sfloat, DstSel(4, 5, 6, 7));
@@ -18767,12 +18833,19 @@ void CheckSampledDepthResource() {
"array shader resource accepted a non-array descriptor view"); "array shader resource accepted a non-array descriptor view");
resource = basic; resource = basic;
resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
Require("SampledDepthResource", "integer rejected", Require("SampledDepthResource", "integer read",
IsSupportedSampledDepthResource(resource),
"read-only uint depth resource was rejected");
resource.depth_compare = true;
Require("SampledDepthResource", "integer comparison rejected",
!IsSupportedSampledDepthResource(resource), !IsSupportedSampledDepthResource(resource),
"integer depth resource was accepted"); "uint depth resource accepted comparison sampling");
Require("SampledDepthResource", "integer reinterpret resource", Require("SampledDepthResource", "R32 uint depth view",
IsSupportedSampledDepthUintResource(resource), IsSupportedSampledDepthFormat(vk::Format::eD32Sfloat,
"read-only uint depth reinterpretation resource was rejected"); vk::Format::eR32Uint) &&
!IsSupportedSampledDepthFormat(vk::Format::eR32Uint,
vk::Format::eR32Uint),
"R32 uint depth-view compatibility diverged from its backing role");
std::printf("[host] %-32s ok\n", "SampledDepthResource"); std::printf("[host] %-32s ok\n", "SampledDepthResource");
} }