mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
support storage descriptors and tracked image aliases
This commit is contained in:
@@ -156,9 +156,60 @@ NativeSamplerDescriptor(const ShaderRecompiler::IR::DescriptorValue& descriptor)
|
||||
}
|
||||
|
||||
static BufferView NativeStorageBuffer(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
const ShaderBufferResource& descriptor,
|
||||
const ShaderRecompiler::IR::BufferResource& resource) {
|
||||
const ShaderBufferResource& descriptor,
|
||||
const ShaderRecompiler::IR::BufferResource& resource) {
|
||||
BufferView result;
|
||||
// Bind a null buffer when these four dwords are
|
||||
// the tracked prefix of an active image sharp. Image validity is encoded by dword 3 bit 31.
|
||||
if (resource.image_alias != ShaderRecompiler::IR::BufferResource::NoImageAlias &&
|
||||
(descriptor.Type() & 2u) != 0) {
|
||||
BindNullStorageBuffer(command_buffer, &result);
|
||||
return result;
|
||||
}
|
||||
// Buffer TYPE is zero. A nonzero value means a buffer instruction received the first four
|
||||
// dwords of an image sharp without a tracked image alias; support the known write-only path.
|
||||
if (descriptor.Type() != 0) {
|
||||
ShaderTextureResource texture {};
|
||||
std::copy_n(descriptor.fields, 4, texture.fields);
|
||||
const auto width = static_cast<uint32_t>(texture.Width5()) + 1u;
|
||||
const auto height = static_cast<uint32_t>(texture.Height5()) + 1u;
|
||||
const auto format = texture.Format();
|
||||
const auto tile = texture.TileMode();
|
||||
const auto type = texture.Type();
|
||||
const auto address = texture.Base40();
|
||||
const auto pitch = TileGetTexturePitch(format, width, 1, tile);
|
||||
TileSizeAlign footprint {};
|
||||
TileGetTextureTotalSize(format, width, height, 1, pitch, 1, tile, false, &footprint);
|
||||
const bool supported =
|
||||
resource.formatted && resource.written && !resource.read && !resource.atomic &&
|
||||
format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UInt) &&
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
|
||||
type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
texture.DstSelXYZW() == DstSel(4, 5, 6, 7) && address != 0 && footprint.size != 0 &&
|
||||
footprint.align == 65536;
|
||||
if (!supported) {
|
||||
EXIT("unsupported texture descriptor used as storage buffer: type=%u format=%u"
|
||||
" tile=%u swizzle=0x%03x extent=%ux%u read=%d written=%d formatted=%d atomic=%d\n",
|
||||
type, format, tile, texture.DstSelXYZW(), width, height, resource.read,
|
||||
resource.written, resource.formatted, resource.atomic);
|
||||
}
|
||||
auto* ctx = g_render_ctx->GetGraphicCtx();
|
||||
g_render_ctx->GetBufferCache()->ValidateGpuAccess(address, footprint.size, false, true);
|
||||
auto binding = g_render_ctx->GetBufferCache()->ObtainBuffer(
|
||||
command_buffer, ctx, address, footprint.size, true, false, true);
|
||||
const auto alignment = ctx->StorageMinAlignment();
|
||||
if (alignment == 0 || binding.second % alignment != 0 ||
|
||||
footprint.size > ctx->GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
||||
EXIT("texture-backed storage buffer binding is unsupported: addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 " offset=0x%016" PRIx64 " alignment=0x%016" PRIx64 "\n",
|
||||
address, footprint.size, static_cast<uint64_t>(binding.second),
|
||||
static_cast<uint64_t>(alignment));
|
||||
}
|
||||
result.buffer = binding.first;
|
||||
result.offset = binding.second;
|
||||
result.range = footprint.size;
|
||||
return result;
|
||||
}
|
||||
const auto address = descriptor.Base48();
|
||||
const auto stride = descriptor.Stride();
|
||||
const auto records = descriptor.NumRecords();
|
||||
@@ -247,7 +298,9 @@ static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageRes
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return resource.kind == ShaderRecompiler::IR::ResourceKind::Image && supported_dimension &&
|
||||
const bool sampled_kind = resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
return sampled_kind && supported_dimension &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
!resource.written && !resource.atomic && !resource.depth_compare;
|
||||
}
|
||||
@@ -268,6 +321,10 @@ ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D, 0, 1}
|
||||
: TargetTextureViewInfo {};
|
||||
case Prospero::ImageType::kColor2DArray:
|
||||
if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
base_layer == 0 && image_layers == 1) {
|
||||
return {VK_IMAGE_VIEW_TYPE_2D, 0, 1};
|
||||
}
|
||||
return resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
|
||||
base_layer < image_layers
|
||||
? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D_ARRAY, base_layer,
|
||||
@@ -321,20 +378,21 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
||||
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u, 1,
|
||||
descriptor.TileMode());
|
||||
EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d "
|
||||
"kind=%u dimension=%u mip=%u read=%d written=%d atomic=%d compare=%d "
|
||||
"image_format=%d view_format=%d image_layers=%u descriptor_type=%u base_array=%u "
|
||||
"depth=%u descriptor_pitch=%u target_pitch=%u fields=%08x/%08x/%08x/%08x/%08x/"
|
||||
"%08x/%08x/%08x addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
resource_ok, descriptor_ok, encoding_ok, format_ok,
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, resource.atomic,
|
||||
resource.depth_compare,
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d "
|
||||
"guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u "
|
||||
"descriptor_type=%u base_array=%u depth=%u descriptor_pitch=%u target_pitch=%u "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
|
||||
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind),
|
||||
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
|
||||
resource.read, resource.written, resource.atomic, resource.depth_compare,
|
||||
descriptor.Format(), descriptor.DstSelXYZW(),
|
||||
image == nullptr ? static_cast<int>(VK_FORMAT_UNDEFINED) : static_cast<int>(image->format),
|
||||
static_cast<int>(view_format), image == nullptr ? 0u : image->layers, descriptor.Type(),
|
||||
descriptor.BaseArray5(), descriptor.Depth(), descriptor_pitch,
|
||||
image == nullptr ? 0u : image->guest_pitch, descriptor.fields[0], descriptor.fields[1],
|
||||
descriptor.fields[2], descriptor.fields[3], descriptor.fields[4], descriptor.fields[5],
|
||||
descriptor.fields[6], descriptor.fields[7], descriptor.Base40(), size);
|
||||
image == nullptr ? 0u : image->guest_pitch, descriptor.Base40(), size,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
@@ -362,8 +420,10 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
const bool supported_swizzle =
|
||||
IsSupportedStorageSwizzle(descriptor.Format(), descriptor.DstSelXYZW()) &&
|
||||
(descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read);
|
||||
return (is_2d || is_2d_array || is_3d) && supported_tile && descriptor.BaseLevel() == 0 &&
|
||||
descriptor.LastLevel() == 0 && descriptor.MaxMip() == 0 && descriptor.MinLod() == 0 &&
|
||||
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_2d;
|
||||
return (is_2d || is_2d_array || is_3d) && supported_tile && supported_mip_view &&
|
||||
descriptor.BaseLevel() == descriptor.LastLevel() &&
|
||||
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
|
||||
descriptor.BaseArray5() == 0 && supported_swizzle && descriptor.BCSwizzle() == 0 &&
|
||||
!descriptor.MsaaDepth();
|
||||
}
|
||||
@@ -372,14 +432,17 @@ static bool IsSupportedStorageTextureEncoding(const ShaderTextureResource& descr
|
||||
constexpr uint32_t field1_reserved_mask = 0x200fff00u;
|
||||
constexpr uint32_t field2_reserved_mask = 0xf0003000u;
|
||||
constexpr uint32_t field5_expected = 0x00700000u;
|
||||
constexpr uint32_t field5_max_mip_mask = 0x000000f0u;
|
||||
const uint32_t expected_field3 = descriptor.DstSelXYZW() |
|
||||
(static_cast<uint32_t>(descriptor.BaseLevel()) << 12u) |
|
||||
(static_cast<uint32_t>(descriptor.LastLevel()) << 16u) |
|
||||
(static_cast<uint32_t>(descriptor.TileMode()) << 20u) |
|
||||
(static_cast<uint32_t>(descriptor.Type()) << 28u);
|
||||
return (descriptor.fields[1] & field1_reserved_mask) == 0 &&
|
||||
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
|
||||
descriptor.fields[3] == expected_field3 && descriptor.fields[4] == descriptor.Depth() &&
|
||||
descriptor.fields[5] == field5_expected && descriptor.fields[6] == 0 &&
|
||||
descriptor.fields[7] == 0;
|
||||
(descriptor.fields[5] & ~field5_max_mip_mask) == field5_expected &&
|
||||
descriptor.fields[6] == 0 && descriptor.fields[7] == 0;
|
||||
}
|
||||
|
||||
void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
@@ -396,13 +459,28 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
return;
|
||||
}
|
||||
EXIT("unsupported storage texture: resource=%d descriptor=%d encoding=%d format=%d "
|
||||
"kind=%u dimension=%u mip_mode=%u atomic=%d compare=%d "
|
||||
"base_level=%u last_level=%u max_mip=%u min_lod=%u base_array=%u bc=%u msaa=%d "
|
||||
"depth_tile_shape=%d swizzle_ok=%d "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d\n",
|
||||
resource_ok, descriptor_ok, encoding_ok, format_ok, descriptor.Base40(), size,
|
||||
static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
" extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d "
|
||||
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
|
||||
resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast<uint32_t>(resource.kind),
|
||||
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
|
||||
resource.atomic, resource.depth_compare, descriptor.BaseLevel(), descriptor.LastLevel(),
|
||||
descriptor.MaxMip(), descriptor.MinLod(), descriptor.BaseArray5(), descriptor.BCSwizzle(),
|
||||
descriptor.MsaaDepth(),
|
||||
IsSupportedStorageDepthTile(descriptor.Format(), descriptor.Type(),
|
||||
static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Height5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Depth()) + 1u),
|
||||
IsSupportedStorageSwizzle(descriptor.Format(), descriptor.DstSelXYZW()),
|
||||
descriptor.Base40(), size, static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Height5()) + 1u,
|
||||
static_cast<uint32_t>(descriptor.Depth()) + 1u, descriptor.Type(), format,
|
||||
descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written);
|
||||
descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
void ValidateMetadataReuseTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
@@ -451,6 +529,16 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
|
||||
const auto tile = descriptor.TileMode();
|
||||
const auto format = descriptor.Format();
|
||||
const bool sampled_numeric_class =
|
||||
storage || ((resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
|
||||
Prospero::IsUintTextureFormat(format));
|
||||
if (!storage &&
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) &&
|
||||
!sampled_numeric_class) {
|
||||
EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n",
|
||||
static_cast<uint32_t>(resource.kind), format, address);
|
||||
}
|
||||
const auto view_format = TextureGetFormat(format, storage ? TextureFormatUsage::Storage
|
||||
: TextureFormatUsage::Sampled);
|
||||
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
|
||||
@@ -476,37 +564,48 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
descriptor.MsaaDepth();
|
||||
if (image == nullptr) {
|
||||
if (check_depth) {
|
||||
image = g_render_ctx->GetTextureCache()->FindDepthTargetByRange(address, size.size);
|
||||
image =
|
||||
g_render_ctx->GetTextureCache()->FindDepthTargetByRange(address, size.size, true);
|
||||
} else {
|
||||
image = g_render_ctx->GetTextureCache()->FindRenderTargetByRange(command_buffer,
|
||||
address, size.size);
|
||||
}
|
||||
if (image != nullptr) {
|
||||
if (check_depth) {
|
||||
ValidateDepthTargetBinding(resource, descriptor, image, view_format, size.size);
|
||||
view = SelectSampledDepthView(image->format, view_format, swizzle);
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
static std::atomic_uint log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) {
|
||||
LOGF("SampledDepthTarget: bind addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" format=%d view=%d\n",
|
||||
address, size.size, static_cast<int>(image->format), view);
|
||||
}
|
||||
const bool uint_reinterpret =
|
||||
IsSupportedSampledDepthUintResource(resource) &&
|
||||
IsSupportedDepthTargetDescriptor(descriptor, *image) &&
|
||||
IsSupportedDepthTextureEncoding(descriptor) &&
|
||||
IsDepthUintTextureReinterpretation(image->format, descriptor.Format(),
|
||||
view_format);
|
||||
const bool uint_storage_reinterpret =
|
||||
storage && IsSupportedStorageImageResource(resource) &&
|
||||
IsSupportedStorageTextureDescriptor(resource, descriptor) &&
|
||||
IsSupportedStorageTextureEncoding(descriptor) &&
|
||||
IsDepthUintTextureReinterpretation(image->format, descriptor.Format(),
|
||||
view_format);
|
||||
if (uint_reinterpret || uint_storage_reinterpret) {
|
||||
image = nullptr;
|
||||
} else {
|
||||
ValidateDepthTargetBinding(resource, descriptor, image, view_format, size.size);
|
||||
view = SelectSampledDepthView(image->format, view_format, swizzle);
|
||||
}
|
||||
} else {
|
||||
if (!(storage ? IsSupportedStorageImageResource(resource)
|
||||
: IsSupportedSampledColorResource(resource)) ||
|
||||
image->type != VulkanImageType::RenderTexture || width != image->extent.width ||
|
||||
height != image->extent.height ||
|
||||
(storage ? levels != 1 || base_level != 0
|
||||
(storage ? levels != image->mip_levels || base_level != 0
|
||||
: levels != image->mip_levels || base_level >= levels) ||
|
||||
target_view.type == VK_IMAGE_VIEW_TYPE_MAX_ENUM ||
|
||||
target_view.base_layer >= image->layers ||
|
||||
target_view.layer_count > image->layers - target_view.base_layer) {
|
||||
EXIT("unsupported cached render-target image view: storage=%d resource=%u"
|
||||
EXIT("unsupported cached render-target image view: storage=%d resource=%u "
|
||||
"dimension=%u"
|
||||
" image_type=%u layers=%u extent=%ux%u/%ux%u depth=%u"
|
||||
" levels=%u/%u base_level=%u base_array=%u descriptor_type=%u\n",
|
||||
storage, static_cast<uint32_t>(resource.kind),
|
||||
static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(image->type), image->layers, width, height,
|
||||
image->extent.width, image->extent.height, depth, levels,
|
||||
image->mip_levels, base_level, descriptor.BaseArray5(),
|
||||
@@ -528,10 +627,10 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
target_view.layer_count);
|
||||
}
|
||||
}
|
||||
if (image_view == nullptr && image->image_view[view] == nullptr) {
|
||||
if (image != nullptr && image_view == nullptr && image->image_view[view] == nullptr) {
|
||||
EXIT("required cached texture image view is missing\n");
|
||||
}
|
||||
if (storage) {
|
||||
if (storage && image != nullptr) {
|
||||
g_render_ctx->GetTextureCache()->MarkGpuWritten(image);
|
||||
}
|
||||
}
|
||||
@@ -540,11 +639,32 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
const auto video = Presentation::DisplayBufferFind(address);
|
||||
if (video.image != nullptr) {
|
||||
if (storage) {
|
||||
EXIT("storage access to a video-out surface is unsupported\n");
|
||||
}
|
||||
image = video.image;
|
||||
if (!storage && swizzle == DstSel(6, 5, 4, 7)) {
|
||||
view = VulkanImage::VIEW_BGRA;
|
||||
const bool exact =
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
!resource.read && resource.written && !resource.atomic &&
|
||||
format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UInt) &&
|
||||
view_format == VK_FORMAT_R8G8B8A8_UINT && swizzle == DstSel(6, 5, 4, 7) &&
|
||||
width == video.image->extent.width && height == video.image->extent.height &&
|
||||
depth == 1 && levels == 1 && base_level == 0 && view_levels == 1 &&
|
||||
type == Prospero::ImageType::kColor2D && video.size == size.size &&
|
||||
video.pitch == pitch &&
|
||||
(video.image->format == VK_FORMAT_R8G8B8A8_SRGB ||
|
||||
video.image->format == VK_FORMAT_B8G8R8A8_SRGB) &&
|
||||
video.image->image_view[VulkanImage::VIEW_STORAGE] != nullptr;
|
||||
if (!exact) {
|
||||
EXIT("unsupported storage access to video-out surface: format=%u view=%d"
|
||||
" extent=%ux%u size=0x%016" PRIx64 " pitch=%u\n",
|
||||
format, static_cast<int>(view_format), width, height, size.size, pitch);
|
||||
}
|
||||
image = video.image;
|
||||
view = VulkanImage::VIEW_STORAGE;
|
||||
g_render_ctx->GetTextureCache()->MarkGpuWritten(image);
|
||||
} else {
|
||||
image = video.image;
|
||||
if (swizzle == DstSel(6, 5, 4, 7)) {
|
||||
view = VulkanImage::VIEW_BGRA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -578,6 +698,9 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
image = texture_cache->FindStorageTexture(command_buffer, g_render_ctx->GetGraphicCtx(),
|
||||
info);
|
||||
view = VulkanImage::VIEW_DEFAULT;
|
||||
image_view = texture_cache->GetStorageTextureStorageView(
|
||||
g_render_ctx->GetGraphicCtx(), static_cast<StorageTextureVulkanImage*>(image),
|
||||
base_level);
|
||||
} else {
|
||||
image = texture_cache->FindTexture(command_buffer, g_render_ctx->GetGraphicCtx(), info,
|
||||
metadata_read);
|
||||
@@ -589,7 +712,9 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(image == nullptr);
|
||||
if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) {
|
||||
if (NeedsStaticSampledArrayView(resource.dimension ==
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray,
|
||||
image_view != nullptr)) {
|
||||
view = SelectSampledTextureArrayView(image, view);
|
||||
}
|
||||
return {image, view, image_view};
|
||||
|
||||
@@ -111,7 +111,7 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor
|
||||
}
|
||||
}
|
||||
|
||||
const bool size_xy_valid = z.size.valid && (z.size.x_max != 0 || z.size.y_max != 0);
|
||||
const bool size_xy_valid = z.size.valid;
|
||||
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
|
||||
if (!size_xy_valid && !wh_valid) {
|
||||
// Prospero emits an exact full-surface metadata descriptor even
|
||||
@@ -281,18 +281,6 @@ static bool TryConsumeComputeMetaClear(const ShaderComputeInputInfo& input, cons
|
||||
if (!cache->ClearMeta(target.address)) {
|
||||
EXIT("failed to record HTile compute clear\n");
|
||||
}
|
||||
const bool descriptor_backed =
|
||||
current_references != 0 && !z.size.valid && !z.width_height_valid && !z.pitch_height_valid;
|
||||
const uint32_t source_bit = current_references == 0 ? 1u : descriptor_backed ? 2u : 4u;
|
||||
static std::atomic<uint32_t> logged_sources {0};
|
||||
if ((logged_sources.fetch_or(source_bit, std::memory_order_relaxed) & source_bit) == 0) {
|
||||
LOGF("HTileClear: native virtual clear source=%s shader=0x%016" PRIx64 " addr=0x%016" PRIx64
|
||||
" size=0x%016" PRIx64 "\n",
|
||||
current_references == 0 ? "registered"
|
||||
: descriptor_backed ? "descriptor"
|
||||
: "derived",
|
||||
program.shader_hash, target.address, target.size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,15 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||
const auto alias = program.info.buffers[i].image_alias;
|
||||
if (alias != BufferResource::NoImageAlias && alias >= program.info.images.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("buffer resource {} has invalid image alias {}", i, alias);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto CheckWidth = [&](const auto& values, uint32_t width, const char* kind) {
|
||||
for (uint32_t i = 0; i < values.size(); i++) {
|
||||
if (values[i].dword_count != width) {
|
||||
|
||||
@@ -178,6 +178,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
LinkImageAliases();
|
||||
m_program.info = std::move(m_info);
|
||||
for (const auto& patch: m_patches) {
|
||||
patch.inst->memory.resource = patch.resource;
|
||||
@@ -290,6 +291,24 @@ private:
|
||||
resource->scalar = resource->scalar || inst.memory.kind == ResourceKind::ScalarBuffer;
|
||||
}
|
||||
|
||||
void LinkImageAliases() {
|
||||
for (auto& buffer: m_info.buffers) {
|
||||
const auto* buffer_source = GetDescriptorSource(m_program, buffer.source);
|
||||
if (buffer_source == nullptr || buffer_source->dword_count != 4) {
|
||||
continue;
|
||||
}
|
||||
for (uint32_t i = 0; i < m_info.images.size(); i++) {
|
||||
const auto* image_source = GetDescriptorSource(m_program, m_info.images[i].source);
|
||||
if (image_source != nullptr && image_source->dword_count == 8 &&
|
||||
std::equal(buffer_source->dwords.begin(), buffer_source->dwords.begin() + 4,
|
||||
image_source->dwords.begin())) {
|
||||
buffer.image_alias = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t AddImage(const Instruction& inst) {
|
||||
for (uint32_t i = 0; i < m_info.images.size(); i++) {
|
||||
auto& image = m_info.images[i];
|
||||
|
||||
@@ -575,11 +575,14 @@ struct SrtPlan {
|
||||
};
|
||||
|
||||
struct BufferResource {
|
||||
static constexpr uint32_t NoImageAlias = UINT32_MAX;
|
||||
|
||||
uint32_t source = 0;
|
||||
uint32_t first_use_pc = 0;
|
||||
uint32_t max_byte_extent = 0;
|
||||
uint32_t packed_stride = 0;
|
||||
uint32_t descriptor_format = 0;
|
||||
uint32_t image_alias = NoImageAlias;
|
||||
bool read = false;
|
||||
bool written = false;
|
||||
bool atomic = false;
|
||||
|
||||
@@ -64,6 +64,7 @@ struct ShaderBufferResource {
|
||||
}
|
||||
[[nodiscard]] uint8_t Format() const { return (fields[3] >> 12u) & 0x7Fu; }
|
||||
[[nodiscard]] uint8_t OutOfBounds() const { return (fields[3] >> 28u) & 0x3u; }
|
||||
[[nodiscard]] uint8_t Type() const { return (fields[3] >> 30u) & 0x3u; }
|
||||
};
|
||||
|
||||
struct ShaderTextureResource {
|
||||
|
||||
@@ -227,6 +227,21 @@ void TestScalarAndVectorBufferAlias() {
|
||||
"scalar/vector alias did not share one dense index");
|
||||
}
|
||||
|
||||
void TestBufferImageAliasIsLinkedDuringTracking() {
|
||||
Program program;
|
||||
program.blocks.resize(1);
|
||||
program.blocks[0].instructions = {
|
||||
BufferUse(4, 0), BufferUse(8, 8),
|
||||
ImageUse(12, Opcode::ImageLoad, ResourceKind::Image,
|
||||
Decoder::ImageDimension::Dim2D)};
|
||||
|
||||
Prepare(&program);
|
||||
Check(program.info.buffers.size() == 2 && program.info.images.size() == 1 &&
|
||||
program.info.buffers[0].image_alias == 0 &&
|
||||
program.info.buffers[1].image_alias == BufferResource::NoImageAlias,
|
||||
"buffer/image descriptor provenance aliases were not linked during tracking");
|
||||
}
|
||||
|
||||
void TestImagesAndSamplers() {
|
||||
Program program;
|
||||
program.blocks.resize(1);
|
||||
@@ -1793,6 +1808,7 @@ int main() {
|
||||
try {
|
||||
RUN(TestDenseBufferPatching);
|
||||
RUN(TestScalarAndVectorBufferAlias);
|
||||
RUN(TestBufferImageAliasIsLinkedDuringTracking);
|
||||
RUN(TestImagesAndSamplers);
|
||||
RUN(TestDynamicPhiResource);
|
||||
RUN(TestTrackingRequiresCompletedSrtPlan);
|
||||
|
||||
@@ -8858,6 +8858,17 @@ void CheckSampledColorViews() {
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
DstSel(7, 6, 5, 4)) == VulkanImage::VIEW_ABGR,
|
||||
"reverse RGBA16F target did not select its reciprocal ABGR sampled view");
|
||||
Require("SampledColorViews", "mutable integer-class views",
|
||||
SelectSampledColorView(VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_UINT,
|
||||
DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT &&
|
||||
SelectSampledColorView(VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_FORMAT_R8G8B8A8_UINT,
|
||||
DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT &&
|
||||
SelectSampledColorView(VK_FORMAT_R8G8B8A8_UINT,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT,
|
||||
"compatible integer render-target sampled view was rejected");
|
||||
Require("SampledColorViews", "D32 depth target",
|
||||
SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_R32_SFLOAT,
|
||||
@@ -8905,6 +8916,10 @@ void CheckSampledColorViews() {
|
||||
SelectStorageColorView(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
DstSel(6, 5, 4, 7)) == VulkanImage::VIEW_STORAGE,
|
||||
"RGBA8 BGRA storage did not select the identity storage view");
|
||||
Require("SampledColorViews", "storage uint BGRA write mapping",
|
||||
SelectStorageColorView(VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UINT,
|
||||
DstSel(6, 5, 4, 7)) == VulkanImage::VIEW_STORAGE,
|
||||
"RGBA8_UINT BGRA storage did not select the identity storage view");
|
||||
Require(
|
||||
"SampledColorViews", "mutable BGRA sRGB storage view",
|
||||
SelectStorageColorView(VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
@@ -8964,7 +8979,7 @@ void CheckSampledColorViews() {
|
||||
"GetModuleFileName failed");
|
||||
for (const char *kind :
|
||||
{"sampled", "sampled-compatible-swizzle", "sampled-compatible-reverse",
|
||||
"sampled-compatible-class", "sampled-compatible-colorspace",
|
||||
"sampled-compatible-colorspace",
|
||||
"sampled-compatible-snorm", "sampled-rgb10-swizzle",
|
||||
"sampled-rgb10-reverse", "sampled-abgr-format", "sampled-depth-format",
|
||||
"sampled-depth-swizzle", "storage", "storage-format",
|
||||
@@ -9040,6 +9055,17 @@ void CheckSampledDepthResource() {
|
||||
Require("SampledDepthResource", "integer rejected",
|
||||
!IsSupportedSampledDepthResource(resource),
|
||||
"integer depth resource was accepted");
|
||||
Require("SampledDepthResource", "integer reinterpret resource",
|
||||
IsSupportedSampledDepthUintResource(resource),
|
||||
"read-only uint depth reinterpretation resource was rejected");
|
||||
Require("SampledDepthResource", "D32 to R32_UINT",
|
||||
IsDepthUintTextureReinterpretation(
|
||||
VK_FORMAT_D32_SFLOAT,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), VK_FORMAT_R32_UINT) &&
|
||||
!IsDepthUintTextureReinterpretation(
|
||||
VK_FORMAT_D16_UNORM,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), VK_FORMAT_R32_UINT),
|
||||
"depth uint reinterpretation format boundary changed");
|
||||
std::printf("[host] %-32s ok\n", "SampledDepthResource");
|
||||
}
|
||||
|
||||
@@ -9074,6 +9100,53 @@ void CheckSampledDepthDescriptor() {
|
||||
Require("SampledDepthDescriptor", "non-depth tile",
|
||||
!IsSupportedDepthTargetDescriptor(descriptor, image),
|
||||
"non-depth texture descriptor was accepted");
|
||||
|
||||
DepthTargetInfo target{};
|
||||
target.address = 0x4a8c00000;
|
||||
target.size = 0x100000;
|
||||
target.width = 512;
|
||||
target.height = 512;
|
||||
target.pitch = 512;
|
||||
target.layers = 1;
|
||||
target.bytes_per_element = 4;
|
||||
target.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
target.format = VK_FORMAT_D32_SFLOAT;
|
||||
target.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
ImageInfo expanded{};
|
||||
expanded.address = target.address;
|
||||
expanded.size = 0x400000;
|
||||
expanded.format = target.guest_format;
|
||||
expanded.width = target.width;
|
||||
expanded.height = target.height;
|
||||
expanded.pitch = target.pitch;
|
||||
expanded.levels = 1;
|
||||
expanded.view_levels = 1;
|
||||
expanded.depth = 4;
|
||||
expanded.tile = target.tile_mode;
|
||||
expanded.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray);
|
||||
Require("SampledDepthDescriptor", "PPSA01530 layer expansion",
|
||||
IsSampledDepthExpansion(expanded, target),
|
||||
"exact D32 per-layer expansion was rejected");
|
||||
auto invalid_expansion = expanded;
|
||||
invalid_expansion.pitch *= 2;
|
||||
const bool rejects_pitch = !IsSampledDepthExpansion(invalid_expansion, target);
|
||||
invalid_expansion = expanded;
|
||||
invalid_expansion.address++;
|
||||
const bool rejects_address = !IsSampledDepthExpansion(invalid_expansion, target);
|
||||
invalid_expansion = expanded;
|
||||
invalid_expansion.levels = 2;
|
||||
const bool rejects_mips = !IsSampledDepthExpansion(invalid_expansion, target);
|
||||
invalid_expansion = expanded;
|
||||
invalid_expansion.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
|
||||
const bool rejects_topology = !IsSampledDepthExpansion(invalid_expansion, target);
|
||||
auto metadata_target = target;
|
||||
metadata_target.htile_address = 0x4a9c00000;
|
||||
metadata_target.htile_size = 0x10000;
|
||||
const bool rejects_metadata = !IsSampledDepthExpansion(expanded, metadata_target);
|
||||
Require("SampledDepthDescriptor", "expansion boundaries",
|
||||
rejects_pitch && rejects_address && rejects_mips && rejects_topology &&
|
||||
rejects_metadata,
|
||||
"unsupported depth expansion topology was accepted");
|
||||
std::printf("[host] %-32s ok\n", "SampledDepthDescriptor");
|
||||
}
|
||||
|
||||
@@ -9144,6 +9217,17 @@ ShaderTextureResource BasicBgraStorageTextureDescriptor() {
|
||||
0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}};
|
||||
}
|
||||
|
||||
ShaderTextureResource Ppsa01530MaxMipStorageTextureDescriptor() {
|
||||
return {{0x04a42900u, 0xc3e00000u, 0x000fc00fu, 0x91b0022cu,
|
||||
0x00000000u, 0x00700050u, 0x00000000u, 0x00000000u}};
|
||||
}
|
||||
|
||||
ShaderRecompiler::IR::ImageResource Ppsa01530MaxMipStorageTextureResource() {
|
||||
auto resource = BasicBgraStorageTextureResource();
|
||||
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
return resource;
|
||||
}
|
||||
|
||||
ShaderTextureResource Ppsa02527R16FloatStorageTextureDescriptor() {
|
||||
return {{0x00ce3500u, 0xc0d00000u, 0x010dc1dfu, 0x91b00204u,
|
||||
0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}};
|
||||
@@ -9255,6 +9339,11 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() {
|
||||
resource = BasicArrayStorageTextureResource();
|
||||
descriptor = BasicArrayStorageTextureDescriptor();
|
||||
descriptor.fields[4] |= 1u << 16u;
|
||||
} else if (std::strcmp(kind, "array-mip-view") == 0) {
|
||||
resource = BasicArrayStorageTextureResource();
|
||||
descriptor = BasicArrayStorageTextureDescriptor();
|
||||
descriptor.fields[3] |= (1u << 12u) | (1u << 16u);
|
||||
descriptor.fields[5] |= 1u << 4u;
|
||||
} else if (std::strcmp(kind, "reserved") == 0) {
|
||||
descriptor.fields[1] |= 1u << 29u;
|
||||
} else if (std::strcmp(kind, "metadata") == 0) {
|
||||
@@ -9308,6 +9397,26 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
"PPSA02604 BGRA 2D storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(BasicBgraStorageTextureResource(), bgra, 0x870000);
|
||||
|
||||
const auto max_mip = Ppsa01530MaxMipStorageTextureDescriptor();
|
||||
Require("BasicStorageTexture", "PPSA01530 max-mip descriptor",
|
||||
max_mip.Base40() == 0x4a4290000ull && max_mip.Width5() + 1u == 64 &&
|
||||
max_mip.Height5() + 1u == 64 && max_mip.Depth() + 1u == 1 &&
|
||||
max_mip.BaseLevel() == 0 && max_mip.LastLevel() == 0 &&
|
||||
max_mip.MaxMip() == 5 &&
|
||||
max_mip.Format() ==
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32UInt) &&
|
||||
max_mip.TileMode() ==
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
|
||||
max_mip.DstSelXYZW() == DstSel(4, 5, 0, 1),
|
||||
"PPSA01530 max-mip storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(Ppsa01530MaxMipStorageTextureResource(), max_mip, 0x20000);
|
||||
auto mip_one = max_mip;
|
||||
mip_one.fields[3] |= (1u << 12u) | (1u << 16u);
|
||||
Require("BasicStorageTexture", "PPSA01530 mip-one descriptor",
|
||||
mip_one.BaseLevel() == 1 && mip_one.LastLevel() == 1 && mip_one.MaxMip() == 5,
|
||||
"PPSA01530 mip-one storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(Ppsa01530MaxMipStorageTextureResource(), mip_one, 0x20000);
|
||||
|
||||
const auto r16_float = Ppsa02527R16FloatStorageTextureDescriptor();
|
||||
Require("BasicStorageTexture", "PPSA02527 R16F descriptor",
|
||||
r16_float.Base40() == 0xce350000ull && r16_float.Width5() + 1u == 1920 &&
|
||||
@@ -9404,6 +9513,16 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
depth_tile.DstSelXYZW() == DstSel(4, 0, 0, 1),
|
||||
"PPSA14053 write-only depth-tile storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(Ppsa14053DepthTileStorageTextureResource(), depth_tile, 0x10000);
|
||||
Require("BasicStorageTexture", "D32-compatible R32_UINT depth tile",
|
||||
IsSupportedStorageDepthTile(
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::GpuEnumValue(Prospero::ImageType::kColor2D), 64, 64, 1),
|
||||
"write-only R32_UINT depth-tile recreation was rejected");
|
||||
Require("BasicStorageTexture", "R32_UINT replicated write mapping",
|
||||
IsSupportedStorageSwizzle(
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
DstSel(4, 4, 4, 4)),
|
||||
"single-channel replicated destination selection was rejected");
|
||||
|
||||
char path[MAX_PATH]{};
|
||||
Require("BasicStorageTexture", "host",
|
||||
@@ -9412,7 +9531,7 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
for (const char *kind : {"resource", "type", "tile", "mip", "swizzle",
|
||||
"linear-rgb1-read", "bgra-read", "r16-float-read", "r8-unorm-read",
|
||||
"yzwx-read", "yzwx-format",
|
||||
"array-base-view", "reserved", "metadata", "uint-format",
|
||||
"array-base-view", "array-mip-view", "reserved", "metadata", "uint-format",
|
||||
"uint-resource-float-format", "depth-tile-read",
|
||||
"depth-tile-extent"}) {
|
||||
std::string command = std::string("\"") + path +
|
||||
@@ -9933,6 +10052,75 @@ void CheckRenderTargetTileRoundTrip() {
|
||||
std::printf("[host] %-32s ok\n", "RenderTargetTileRoundTrip");
|
||||
}
|
||||
|
||||
void CheckStorageTextureMipTailRoundTrip() {
|
||||
constexpr uint32_t format =
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32UInt);
|
||||
ImageInfo info{};
|
||||
info.address = 0x4a4290000ull;
|
||||
info.format = format;
|
||||
info.width = 64;
|
||||
info.height = 64;
|
||||
info.pitch = TileGetTexturePitch(
|
||||
format, info.width, 6,
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget));
|
||||
info.levels = 6;
|
||||
info.view_levels = 1;
|
||||
info.tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
info.depth = 1;
|
||||
info.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
|
||||
TileSizeAlign total{};
|
||||
TileSizeOffset levels[16]{};
|
||||
TilePaddedSize padded[16]{};
|
||||
TileGetTextureSize(format, info.width, info.height, info.pitch, info.levels,
|
||||
info.tile, &total, levels, padded);
|
||||
info.size = total.size;
|
||||
Require("StorageTextureMipTail", "layout",
|
||||
info.pitch == 128 && info.size == 0x20000 &&
|
||||
levels[0].src_offset == 0x10000 && levels[0].src_size == 0x10000 &&
|
||||
levels[1].src_offset == 0 && levels[1].src_size == 0x10000,
|
||||
"PPSA01530 mip-tail fixture disagrees with the PS5 layout");
|
||||
auto layout = TextureCalcUploadLayout(
|
||||
format, info.width, info.height, info.levels, info.depth, info.pitch,
|
||||
info.tile, info.size, true, false, false, "StorageTextureMipTailTest");
|
||||
auto regions = TextureBuildUploadRegions(
|
||||
layout, VK_FORMAT_R32G32_UINT, info.width, info.height, info.depth,
|
||||
info.levels, false, false, TextureUploadDestination::MipLevels,
|
||||
TextureUploadSliceLayout::MipChainPerSlice);
|
||||
std::vector<uint8_t> linear(info.size, 0);
|
||||
for (uint32_t level = 0; level < info.levels; level++) {
|
||||
auto ®ion = regions[level];
|
||||
for (uint32_t y = 0; y < region.height; y++) {
|
||||
auto *row = linear.data() + region.offset +
|
||||
static_cast<uint64_t>(y) * region.pitch * 8u;
|
||||
for (uint32_t x = 0; x < region.width * 8u; x++) {
|
||||
row[x] = static_cast<uint8_t>((level * 43u + y * 17u + x * 7u) & 0xffu);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> guest(info.size, 0xcd);
|
||||
Tiler tiler;
|
||||
tiler.TileImage(guest.data(), linear.data(), info);
|
||||
std::vector<uint8_t> restored(info.size, 0);
|
||||
for (uint32_t level = 0; level < info.levels; level++) {
|
||||
const auto ®ion = regions[level];
|
||||
const auto &level_size = layout.level_sizes[level];
|
||||
const auto guest_offset = level_size.src_size != 0 ? level_size.src_offset
|
||||
: level_size.offset;
|
||||
TileConvertTiledToLinearRenderTarget(
|
||||
restored.data() + region.offset, guest.data() + guest_offset,
|
||||
region.width, region.height, region.pitch, 8u, level_size.size,
|
||||
level_size.src_size, level_size.x, level_size.y);
|
||||
for (uint32_t y = 0; y < region.height; y++) {
|
||||
const auto row = region.offset + static_cast<uint64_t>(y) * region.pitch * 8u;
|
||||
Require("StorageTextureMipTail", "contents",
|
||||
std::memcmp(linear.data() + row, restored.data() + row,
|
||||
region.width * 8u) == 0,
|
||||
"multi-mip storage texture did not round-trip through its packed tail");
|
||||
}
|
||||
}
|
||||
std::printf("[host] %-32s ok\n", "StorageTextureMipTail");
|
||||
}
|
||||
|
||||
void CheckDepthTargetTileRoundTrip() {
|
||||
struct Case {
|
||||
uint32_t width;
|
||||
@@ -10247,6 +10435,28 @@ void CheckStorageTextureSampledReuse() {
|
||||
VK_FORMAT_R8G8B8A8_UNORM, true, false, true) ==
|
||||
StorageSampledOverlap::ExactImage,
|
||||
"exact storage backing rejected a distinct sampled descriptor swizzle");
|
||||
ImageInfo depth_uint_storage{};
|
||||
depth_uint_storage.address = 0x4a5c90000;
|
||||
depth_uint_storage.size = 0x10000;
|
||||
depth_uint_storage.format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt);
|
||||
depth_uint_storage.width = 64;
|
||||
depth_uint_storage.height = 64;
|
||||
depth_uint_storage.pitch = 128;
|
||||
depth_uint_storage.levels = 1;
|
||||
depth_uint_storage.view_levels = 1;
|
||||
depth_uint_storage.depth = 1;
|
||||
depth_uint_storage.tile = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
depth_uint_storage.swizzle = DstSel(4, 4, 4, 4);
|
||||
depth_uint_storage.type = image_2d;
|
||||
auto depth_float_sampled = depth_uint_storage;
|
||||
depth_float_sampled.format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
depth_float_sampled.swizzle = DstSel(4, 0, 0, 1);
|
||||
Require("StorageTextureSampledReuse", "PPSA01530 R32 view",
|
||||
ClassifyStorageSampledOverlap(depth_float_sampled, depth_uint_storage,
|
||||
VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_UINT,
|
||||
true, false, true) ==
|
||||
StorageSampledOverlap::ExactImage,
|
||||
"PPSA01530 GPU-owned R32_UINT storage image did not accept its exact R32_SFLOAT view");
|
||||
const auto usage = TextureFormatUsage::Sampled | TextureFormatUsage::Storage;
|
||||
Require("StorageTextureSampledReuse", "usage",
|
||||
(TextureGetUsage(usage) &
|
||||
@@ -10598,6 +10808,8 @@ void CheckBufferImageWrites() {
|
||||
constexpr uint64_t video_size = 0x1fe0000ull;
|
||||
constexpr uint64_t target = 0x1158d80000ull;
|
||||
constexpr uint64_t target_size = 0x870000ull;
|
||||
constexpr uint64_t storage_address = 0x4a4290000ull;
|
||||
constexpr uint64_t storage_size = 0x20000ull;
|
||||
struct Case {
|
||||
const char *name;
|
||||
uint64_t buffer_address;
|
||||
@@ -10681,6 +10893,22 @@ void CheckBufferImageWrites() {
|
||||
Case{"target unaligned", target + 0x100, target_size, target + 0x100,
|
||||
target_size, BufferImageBinding::RenderTarget, true, true,
|
||||
BufferImageWrite::Unsupported},
|
||||
Case{"storage mip page", storage_address + 0x10000, 0x10000, storage_address, storage_size,
|
||||
BufferImageBinding::StorageTexture, true, true,
|
||||
BufferImageWrite::SynchronizeStorageTexture},
|
||||
Case{"storage unformatted", storage_address + 0x10000, 0x10000, storage_address, storage_size,
|
||||
BufferImageBinding::StorageTexture, true, false,
|
||||
BufferImageWrite::Unsupported},
|
||||
Case{"storage unaligned", storage_address + 0x100, 0x10000, storage_address, storage_size,
|
||||
BufferImageBinding::StorageTexture, true, true,
|
||||
BufferImageWrite::Unsupported},
|
||||
Case{"depth exact", 0x4a5c80000ull, 0x10000, 0x4a5c80000ull, 0x10000,
|
||||
BufferImageBinding::DepthTarget, true, true,
|
||||
BufferImageWrite::SynchronizeDepthTarget},
|
||||
Case{"depth partial", 0x4a5c80000ull, 0x8000, 0x4a5c80000ull, 0x10000,
|
||||
BufferImageBinding::DepthTarget, true, true, BufferImageWrite::Unsupported},
|
||||
Case{"depth unformatted", 0x4a5c80000ull, 0x10000, 0x4a5c80000ull, 0x10000,
|
||||
BufferImageBinding::DepthTarget, true, false, BufferImageWrite::Unsupported},
|
||||
};
|
||||
for (const auto &test : cases) {
|
||||
const auto actual = ClassifyBufferImageWrite(
|
||||
@@ -10689,6 +10917,80 @@ void CheckBufferImageWrites() {
|
||||
Require("BufferImageWrite", test.name, actual == test.expected,
|
||||
"buffer/image write classification mismatch");
|
||||
}
|
||||
Require("BufferImageWrite", "storage containing overwrite",
|
||||
ClassifyBufferImageWrite(storage_address - 0x80000, 0x100000, storage_address,
|
||||
storage_size, BufferImageBinding::StorageTexture, false, true,
|
||||
true) == BufferImageWrite::InvalidateStorageTexture,
|
||||
"formatted containing overwrite did not retain refreshable storage ownership");
|
||||
Require("BufferImageWrite", "storage containing overwrite GPU-owned",
|
||||
ClassifyBufferImageWrite(storage_address - 0x80000, 0x100000, storage_address,
|
||||
storage_size, BufferImageBinding::StorageTexture, true, true,
|
||||
false) == BufferImageWrite::Unsupported,
|
||||
"containing overwrite bypassed GPU-owned storage synchronization");
|
||||
Require("BufferImageWrite", "depth repeated buffer overwrite",
|
||||
ClassifyBufferImageWrite(0x4a5cd0000ull, 0x10000, 0x4a5cd0000ull, 0x10000,
|
||||
BufferImageBinding::DepthTarget, false, true, true) ==
|
||||
BufferImageWrite::InvalidateDepthTarget,
|
||||
"buffer-owned exact depth overwrite was rejected");
|
||||
|
||||
// PPSA01530's packed mip0 page is first synchronized out of a GPU-owned storage image,
|
||||
// written through a persistent formatted buffer, and then rebound as the same storage image.
|
||||
const auto partial_refresh = ClassifyStorageBufferRebind(
|
||||
true, false, true, false, false, true);
|
||||
const auto refreshed_reuse = ClassifyStorageBufferRebind(
|
||||
true, true, false, true, false, true);
|
||||
Require("BufferImageWrite", "storage partial lifecycle",
|
||||
partial_refresh == StorageBufferRebind::RefreshFromBacking &&
|
||||
refreshed_reuse == StorageBufferRebind::Reuse,
|
||||
"storage-to-partial-buffer-to-storage lifecycle was rejected");
|
||||
Require("BufferImageWrite", "storage partial incoherent",
|
||||
ClassifyStorageBufferRebind(true, false, true, false, false, false) ==
|
||||
StorageBufferRebind::Unsupported,
|
||||
"incoherent partial storage backing was admitted");
|
||||
Require("BufferImageWrite", "storage contradictory ownership",
|
||||
ClassifyStorageBufferRebind(true, false, true, true, false, true) ==
|
||||
StorageBufferRebind::Unsupported,
|
||||
"contradictory storage tracker ownership was admitted");
|
||||
Require("BufferImageWrite", "storage mip-one cache miss",
|
||||
SelectImageBackingBaseLevel(true, 1) == 0 &&
|
||||
SelectImageBackingBaseLevel(false, 1) == 1,
|
||||
"storage backing creation remained dependent on first-bound view mip");
|
||||
Require("BufferImageWrite", "dynamic one-layer array view",
|
||||
!NeedsStaticSampledArrayView(true, true) &&
|
||||
NeedsStaticSampledArrayView(true, false) &&
|
||||
!NeedsStaticSampledArrayView(false, false),
|
||||
"dynamic sampled array view was replaced by a missing static view");
|
||||
|
||||
DepthTargetInfo old_depth{};
|
||||
old_depth.address = 0x4a5c90000ull;
|
||||
old_depth.size = 0x10000;
|
||||
old_depth.width = old_depth.height = 32;
|
||||
old_depth.layers = 1;
|
||||
DepthTargetInfo cleared_depth = old_depth;
|
||||
cleared_depth.width = cleared_depth.height = 64;
|
||||
cleared_depth.depth_load_clear = true;
|
||||
Require("BufferImageWrite", "cleared depth reinterpretation",
|
||||
ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) ==
|
||||
DepthOverlap::DiscardTarget,
|
||||
"exact cleared depth allocation could not discard its old native shape");
|
||||
cleared_depth.depth_load_clear = false;
|
||||
Require("BufferImageWrite", "loaded depth reinterpretation",
|
||||
ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) ==
|
||||
DepthOverlap::Unsupported,
|
||||
"loaded depth reinterpretation discarded live native contents");
|
||||
RenderTargetInfo color_alias{};
|
||||
color_alias.address = old_depth.address;
|
||||
color_alias.size = old_depth.size;
|
||||
Require("BufferImageWrite", "buffer-owned depth to color",
|
||||
CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, true,
|
||||
color_alias) &&
|
||||
!CanRetireBufferOwnedDepthForRenderTarget(old_depth, true, false, true, true,
|
||||
color_alias),
|
||||
"depth-to-color recreation did not require coherent buffer ownership");
|
||||
Require("BufferImageWrite", "page-neighbor depth to color",
|
||||
!CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, false,
|
||||
color_alias),
|
||||
"same-page byte-disjoint buffer authorized depth-to-color recreation");
|
||||
|
||||
TileSizeAlign storage{};
|
||||
Require("BufferImageWrite", "target layout",
|
||||
@@ -11763,6 +12065,14 @@ void CheckDepthTargetFootprints() {
|
||||
}
|
||||
|
||||
void CheckHtileClearTargetResolution() {
|
||||
HW::DepthDepthSizeXY one_pixel{};
|
||||
one_pixel.valid = true;
|
||||
Require("HtileClearTargetResolution", "1x1 encoded depth extent",
|
||||
one_pixel.valid && one_pixel.x_max + 1u == 1u && one_pixel.y_max + 1u == 1u,
|
||||
"zero DB_DEPTH_SIZE_XY fields were mistaken for an absent register");
|
||||
HW::DepthDepthSizeXY absent{};
|
||||
Require("HtileClearTargetResolution", "absent depth extent", !absent.valid,
|
||||
"an unwritten DB_DEPTH_SIZE_XY register was admitted");
|
||||
HW::DepthRenderTarget descriptor_backed{};
|
||||
descriptor_backed.z_info.format = Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16);
|
||||
descriptor_backed.z_info.tile_surface_enable = true;
|
||||
@@ -12041,6 +12351,7 @@ int main(int argc, char **argv) {
|
||||
if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) {
|
||||
CheckColorResolveLayers();
|
||||
CheckRenderTargetTileRoundTrip();
|
||||
CheckStorageTextureMipTailRoundTrip();
|
||||
CheckDepthTargetTileRoundTrip();
|
||||
CheckGpuMetadataReuse();
|
||||
return 0;
|
||||
@@ -12115,6 +12426,7 @@ int main(int argc, char **argv) {
|
||||
CheckColorResolveLayers();
|
||||
CheckStandard64RenderTargetTileRoundTrip();
|
||||
CheckRenderTargetTileRoundTrip();
|
||||
CheckStorageTextureMipTailRoundTrip();
|
||||
CheckDepthTargetTileRoundTrip();
|
||||
CheckStencilTargetTileRoundTrip();
|
||||
CheckStorageTextureVolumeUploadLayout();
|
||||
|
||||
Reference in New Issue
Block a user