render: clean up texture transfers and compute fills

This commit is contained in:
nmzik
2026-09-05 13:20:52 +04:00
parent e7fe8192bb
commit d5b19850fb
6 changed files with 141 additions and 192 deletions
+40 -49
View File
@@ -130,10 +130,11 @@ bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& request
TextureCache::BindingType TextureCache::UploadBinding(const Image& image) { TextureCache::BindingType TextureCache::UploadBinding(const Image& image) {
if (image.info.IsDepth()) { if (image.info.IsDepth()) {
return image.info.tile_mode == Prospero::TileMode::kDepth || if (image.info.tile_mode == Prospero::TileMode::kDepth ||
image.info.tile_mode == Prospero::TileMode::kLinear image.info.tile_mode == Prospero::TileMode::kLinear) {
? BindingType::DepthTarget return BindingType::DepthTarget;
: BindingType::Texture; }
return BindingType::Texture;
} }
if (image.usage.render_target) { if (image.usage.render_target) {
return BindingType::RenderTarget; return BindingType::RenderTarget;
@@ -823,7 +824,6 @@ struct TextureCache::TextureTransferPlan {
std::vector<vk::BufferImageCopy> regions; std::vector<vk::BufferImageCopy> regions;
std::vector<GpuTileInfo> tiles; std::vector<GpuTileInfo> tiles;
uint64_t linear_size = 0; uint64_t linear_size = 0;
bool tiled = false;
bool swap_bgra16 = false; bool swap_bgra16 = false;
bool valid = false; bool valid = false;
}; };
@@ -846,52 +846,44 @@ TextureCache::TextureTransferPlan
TextureCache::BuildTextureTransfer(const Image& image, BindingType binding, TextureCache::BuildTextureTransfer(const Image& image, BindingType binding,
TransferDirection direction) const { TransferDirection direction) const {
const auto& info = image.info; const auto& info = image.info;
const bool upload = direction == TransferDirection::Upload;
const bool render_target = binding == BindingType::RenderTarget;
const bool video_out = binding == BindingType::VideoOut;
auto format = info.guest_format; auto format = info.guest_format;
uint32_t layers = info.TransferLayers(); uint32_t layers = info.TransferLayers();
bool volume = info.IsVolume(); bool volume = info.IsVolume();
bool allow_depth_tile = direction == TransferDirection::Upload; bool allow_depth_tile = upload;
const char* owner = const char* owner = "TextureCache readback";
direction == TransferDirection::Upload ? "TextureCache" : "TextureCache readback";
TextureTransferPlan plan; TextureTransferPlan plan;
if (direction == TransferDirection::Upload) { plan.swap_bgra16 = info.bgra16 && (!upload || render_target || video_out);
switch (binding) { if (render_target) {
case BindingType::Texture: break; format = ImageOps::RenderTargetTransferFormat(info.bytes_per_block);
case BindingType::Storage: owner = "StorageTextureCache"; break; }
case BindingType::RenderTarget: if (video_out) {
if (info.resources.layers == 0 || info.data.size % info.resources.layers != 0 || allow_depth_tile = false;
info.samples != 1 || image.backing.samples != 1) { } else if (render_target || binding == BindingType::Storage) {
EXIT("TextureCache: invalid color-attachment upload\n"); allow_depth_tile = true;
} }
format = ImageOps::RenderTargetTransferFormat(info.bytes_per_block); if (upload) {
allow_depth_tile = true; if ((render_target || video_out) &&
plan.swap_bgra16 = info.bgra16; (info.resources.layers == 0 || info.data.size % info.resources.layers != 0 ||
owner = "RenderTarget"; info.samples != 1 || image.backing.samples != 1)) {
break; EXIT("TextureCache: invalid color-attachment upload\n");
case BindingType::VideoOut:
if (info.resources.layers == 0 || info.data.size % info.resources.layers != 0 ||
info.samples != 1 || image.backing.samples != 1 ||
info.metadata.compression != VideoOutCompression::Uncompressed) {
EXIT("TextureCache: invalid color-attachment upload\n");
}
format = info.guest_format;
layers = info.resources.layers;
volume = false;
allow_depth_tile = false;
plan.swap_bgra16 = info.bgra16;
owner = "VideoOut";
break;
case BindingType::DepthTarget: return plan;
} }
} else { owner = "TextureCache";
if (binding == BindingType::DepthTarget) { if (render_target) {
return plan; owner = "RenderTarget";
} else if (binding == BindingType::Storage) {
owner = "StorageTextureCache";
} else if (video_out) {
if (info.metadata.compression != VideoOutCompression::Uncompressed) {
EXIT("TextureCache: invalid color-attachment upload\n");
}
layers = info.resources.layers;
volume = false;
owner = "VideoOut";
} }
format = binding == BindingType::RenderTarget
? ImageOps::RenderTargetTransferFormat(info.bytes_per_block)
: info.guest_format;
allow_depth_tile = binding == BindingType::Storage || binding == BindingType::RenderTarget;
plan.swap_bgra16 = info.bgra16;
} }
plan.layout = TextureCalcUploadLayout(format, info.extent.width, info.extent.height, plan.layout = TextureCalcUploadLayout(format, info.extent.width, info.extent.height,
@@ -903,8 +895,7 @@ TextureCache::BuildTextureTransfer(const Image& image, BindingType binding,
region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
} }
} }
plan.tiled = plan.layout.surface.description.tile_mode != Prospero::TileMode::kLinear; if (plan.layout.surface.description.tile_mode != Prospero::TileMode::kLinear) {
if (plan.tiled) {
if (!TextureBuildGpuTileInfos(info.data.size, plan.regions, plan.layout, if (!TextureBuildGpuTileInfos(info.data.size, plan.regions, plan.layout,
info.resources.levels, plan.tiles)) { info.resources.levels, plan.tiles)) {
return plan; return plan;
@@ -959,7 +950,7 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
info.resources.layers, info.samples); info.resources.layers, info.samples);
} }
TileManager::Result linear {source.Handle(), source_offset, info.data.size}; TileManager::Result linear {source.Handle(), source_offset, info.data.size};
if (plan.tiled) { if (!plan.tiles.empty()) {
linear = m_tiler.Detile(source.Handle(), source_offset, info.data.size, linear = m_tiler.Detile(source.Handle(), source_offset, info.data.size,
plan.linear_size, plan.tiles); plan.linear_size, plan.tiles);
} }
@@ -1568,7 +1559,7 @@ void TextureCache::DownloadImageData(Image& image, Buffer& destination, uint64_t
auto& texture = plan.texture; auto& texture = plan.texture;
const auto transform = texture.swap_bgra16 ? TileManager::ColorTransform::SwapBgra16 const auto transform = texture.swap_bgra16 ? TileManager::ColorTransform::SwapBgra16
: TileManager::ColorTransform::None; : TileManager::ColorTransform::None;
if (!texture.tiled) { if (texture.tiles.empty()) {
if (transform == TileManager::ColorTransform::SwapBgra16) { if (transform == TileManager::ColorTransform::SwapBgra16) {
auto linear = m_tiler.GetScratchBuffer(destination_size); auto linear = m_tiler.GetScratchBuffer(destination_size);
image.Download(texture.regions, linear.buffer, 0, linear.size); image.Download(texture.regions, linear.buffer, 0, linear.size);
@@ -1666,7 +1657,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin
if (texture.regions.empty()) { if (texture.regions.empty()) {
return false; return false;
} }
if (texture.tiled) { if (!texture.tiles.empty()) {
texture.tiles.clear(); texture.tiles.clear();
if (!TextureBuildGpuTileInfos(copy_size, texture.regions, texture.layout, levels, if (!TextureBuildGpuTileInfos(copy_size, texture.regions, texture.layout, levels,
texture.tiles)) { texture.tiles)) {
@@ -92,15 +92,6 @@ SelectSampledColorView(vk::Format image_format, vk::Format view_format, uint32_t
} }
} }
[[nodiscard]] inline uint32_t
SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t swizzle) noexcept {
if (IsSupportedSampledDepthView(image_format, view_format, swizzle)) {
return swizzle;
}
EXIT("unsupported sampled depth image view: image_format=%d view_format=%d swizzle=0x%03x\n",
static_cast<int>(image_format), static_cast<int>(view_format), swizzle);
}
[[nodiscard]] inline bool [[nodiscard]] inline bool
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
if (resource.resource_class != ShaderRecompiler::IR::ImageResourceClass::Sampled) { if (resource.resource_class != ShaderRecompiler::IR::ImageResourceClass::Sampled) {
@@ -335,40 +335,36 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
} }
static void ValidateSampledDepthBinding(const ShaderRecompiler::IR::ImageResource& resource, static void ValidateSampledDepthBinding(const ShaderRecompiler::IR::ImageResource& resource,
const ShaderTextureResource& descriptor, const Image* image, const ShaderTextureResource& descriptor, const Image& image,
vk::Format view_format, uint64_t size) { vk::Format view_format, uint64_t size) {
const bool resource_ok = IsSupportedSampledDepthResource(resource); const bool resource_ok = IsSupportedSampledDepthResource(resource);
const bool descriptor_ok = const bool descriptor_ok = IsSupportedSampledDepthDescriptor(descriptor, image, resource.r128);
image != nullptr && IsSupportedSampledDepthDescriptor(descriptor, *image, resource.r128); const bool encoding_ok = IsSupportedDepthTextureEncoding(descriptor, image, resource.r128);
const bool encoding_ok = const bool view_ok =
image != nullptr && IsSupportedDepthTextureEncoding(descriptor, *image, resource.r128); IsSupportedSampledDepthView(image.info.pixel_format, view_format, descriptor.DstSelXYZW());
const bool format_ok = if (resource_ok && descriptor_ok && encoding_ok && view_ok) {
image != nullptr && IsSupportedSampledDepthFormat(image->info.pixel_format, view_format);
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
return; return;
} }
const auto descriptor_pitch = const auto descriptor_pitch =
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u, TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
descriptor.TileMode()); descriptor.TileMode());
EXIT("unsupported sampled depth image: resource=%d descriptor=%d encoding=%d format=%d " EXIT("unsupported sampled depth image: resource=%d descriptor=%d encoding=%d view=%d "
"class=%u numeric=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d " "class=%u numeric=%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 " "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 " "descriptor_type=%u base_array=%u depth=%u descriptor_pitch=%u target_pitch=%u "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "addr=0x%016" PRIx64 " size=0x%016" PRIx64
" dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", " dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
resource_ok, descriptor_ok, encoding_ok, format_ok, resource_ok, descriptor_ok, encoding_ok, view_ok,
static_cast<uint32_t>(resource.resource_class), static_cast<uint32_t>(resource.resource_class),
static_cast<uint32_t>(resource.numeric_class), static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.numeric_class), static_cast<uint32_t>(resource.dimension),
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, resource.atomic, static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written, resource.atomic,
resource.depth_compare, static_cast<uint32_t>(descriptor.Format()), resource.depth_compare, static_cast<uint32_t>(descriptor.Format()),
descriptor.DstSelXYZW(), descriptor.DstSelXYZW(), static_cast<int>(image.info.pixel_format),
image == nullptr ? static_cast<int>(vk::Format::eUndefined) static_cast<int>(view_format), image.info.resources.layers,
: static_cast<int>(image->info.pixel_format),
static_cast<int>(view_format), image == nullptr ? 0u : image->info.resources.layers,
static_cast<uint32_t>(descriptor.Type()), descriptor.BaseArray5(), descriptor.Depth(), static_cast<uint32_t>(descriptor.Type()), descriptor.BaseArray5(), descriptor.Depth(),
descriptor_pitch, image == nullptr ? 0u : image->info.pitch, descriptor.Base40(), size, descriptor_pitch, image.info.pitch, descriptor.Base40(), size, descriptor.fields[0],
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], descriptor.fields[4],
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]); descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
} }
static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource, static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource,
@@ -747,10 +743,12 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
ValidateStorageTexture(resource, descriptor, size.size); ValidateStorageTexture(resource, descriptor, size.size);
} }
const auto* depth_format = resource.depth_compare ? FindGuestDepthFormatPolicy(format) : nullptr; auto pixel_format = surface_format.vk_format;
EXIT_NOT_IMPLEMENTED(resource.depth_compare && depth_format == nullptr); if (resource.depth_compare) {
const auto pixel_format = const auto* depth_format = FindGuestDepthFormatPolicy(format);
depth_format != nullptr ? depth_format->depth_attachment_format : surface_format.vk_format; EXIT_NOT_IMPLEMENTED(depth_format == nullptr);
pixel_format = depth_format->depth_attachment_format;
}
const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt const auto storage_view_format = storage && format == Prospero::BufferFormat::k32SInt
? vk::Format::eR32Uint ? vk::Format::eR32Uint
: SrgbStorageViewFormat(pixel_format); : SrgbStorageViewFormat(pixel_format);
@@ -789,9 +787,7 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
if (storage) { if (storage) {
EXIT("depth target cannot be bound as a storage image\n"); EXIT("depth target cannot be bound as a storage image\n");
} }
ValidateSampledDepthBinding(resource, descriptor, image, pixel_format, size.size); ValidateSampledDepthBinding(resource, descriptor, *image, pixel_format, size.size);
(void)SelectSampledDepthView(image->info.pixel_format, pixel_format,
descriptor.DstSelXYZW());
} else if (storage) { } else if (storage) {
ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW()); ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW());
} else { } else {
+1 -1
View File
@@ -219,7 +219,7 @@ private:
friend struct RenderExecutorTestAccess; friend struct RenderExecutorTestAccess;
}; };
[[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x, [[nodiscard]] bool ResolveComputeBufferFill(const ShaderComputeInputInfo& input, uint32_t group_x,
uint32_t group_y, uint32_t group_z, uint32_t mode, uint32_t group_y, uint32_t group_z, uint32_t mode,
ShaderBufferResource& descriptor, ShaderBufferResource& descriptor,
uint32_t& packed_clear, uint64_t& size); uint32_t& packed_clear, uint64_t& size);
@@ -39,9 +39,6 @@ namespace Libs::Graphics {
static uint64_t BufferDescriptorSize(const ShaderBufferResource& descriptor) { static uint64_t BufferDescriptorSize(const ShaderBufferResource& descriptor) {
const uint64_t records = descriptor.NumRecords(); const uint64_t records = descriptor.NumRecords();
const uint64_t stride = descriptor.Stride(); const uint64_t stride = descriptor.Stride();
if (stride != 0 && records > UINT64_MAX / stride) {
EXIT("compute buffer descriptor footprint overflow\n");
}
return stride == 0 ? records : records * stride; return stride == 0 ? records : records * stride;
} }
@@ -51,9 +48,8 @@ static bool FillSourcesDisjoint(std::span<const ShaderRecompiler::IR::Descriptor
if (i == output_buffer) continue; if (i == output_buffer) continue;
const auto source = DecodeNativeDescriptor<ShaderBufferResource>(sources[i]); const auto source = DecodeNativeDescriptor<ShaderBufferResource>(sources[i]);
const auto bytes = BufferDescriptorSize(source); const auto bytes = BufferDescriptorSize(source);
if (source.Base48() > UINT64_MAX - bytes || if (source.Base48() < destination.End() && destination.address < source.Base48() + bytes)
(source.Base48() < destination.End() && return false;
destination.address < source.Base48() + bytes)) return false;
} }
return true; return true;
} }
@@ -91,30 +87,23 @@ bool RenderExecutor::TryConsumeComputeMetaClear(const ShaderComputeInputInfo& in
return false; return false;
} }
bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x, bool ResolveComputeBufferFill(const ShaderComputeInputInfo& input, uint32_t group_x,
uint32_t group_y, uint32_t group_z, uint32_t mode, uint32_t group_y, uint32_t group_z, uint32_t mode,
ShaderBufferResource& resolved_descriptor, uint32_t& resolved_clear, ShaderBufferResource& resolved_descriptor, uint32_t& resolved_clear,
uint64_t& resolved_size) { uint64_t& resolved_size) {
const auto& program = *input.stage.program;
const auto& resources = input.stage.resources; const auto& resources = input.stage.resources;
const auto& fill = resources.uniform_fill; const auto& fill = resources.uniform_fill;
if (fill.kind != ShaderRecompiler::IR::UniformFillKind::Buffer || fill.words == 0 || if (fill.kind != ShaderRecompiler::IR::UniformFillKind::Buffer) {
fill.words > 4 || fill.resource >= resources.buffers.size() ||
resources.buffers.size() != program.info.buffers.size()) {
return false; return false;
} }
const auto element_size = fill.words * sizeof(uint32_t); const auto element_size = fill.words * sizeof(uint32_t);
const auto& resource = program.info.buffers[fill.resource]; const auto descriptor =
const auto& raw = resources.buffers[fill.resource]; DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[fill.resource]);
const auto descriptor = DecodeNativeDescriptor<ShaderBufferResource>(raw);
constexpr std::array formats { constexpr std::array formats {
Prospero::BufferFormat::k32UInt, Prospero::BufferFormat::k32_32UInt, Prospero::BufferFormat::k32UInt, Prospero::BufferFormat::k32_32UInt,
Prospero::BufferFormat::k32_32_32UInt, Prospero::BufferFormat::k32_32_32_32UInt}; Prospero::BufferFormat::k32_32_32UInt, Prospero::BufferFormat::k32_32_32_32UInt};
if (!resource.formatted || !resource.written || resource.read || resource.atomic || if (descriptor.Stride() != element_size || descriptor.Format() != formats[fill.words - 1] ||
resource.scalar || descriptor.Stride() != element_size || descriptor.SwizzleEnabled() || descriptor.IndexStride() != 0 || descriptor.AddTid() ||
descriptor.Format() != formats[fill.words - 1] || descriptor.SwizzleEnabled() ||
descriptor.IndexStride() != 0 || descriptor.AddTid() ||
resource.packed_stride != descriptor.PackedStride() || raw.dword_count != 4 ||
descriptor.Base48() == 0) { descriptor.Base48() == 0) {
return false; return false;
} }
@@ -128,7 +117,6 @@ bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t grou
: static_cast<uint64_t>(group_x) * input.threads_num[0]; : static_cast<uint64_t>(group_x) * input.threads_num[0];
const auto size = BufferDescriptorSize(descriptor); const auto size = BufferDescriptorSize(descriptor);
if (invocations != descriptor.NumRecords() || size == 0 || size > UINT32_MAX || if (invocations != descriptor.NumRecords() || size == 0 || size > UINT32_MAX ||
descriptor.Base48() > UINT64_MAX - size ||
(input.dispatch_thread_dimensions && (input.dispatch_thread_dimensions &&
(group_x % input.threads_num[0] != 0 || input.dispatch_threads_num[0] != group_x || (group_x % input.threads_num[0] != 0 || input.dispatch_threads_num[0] != group_x ||
input.dispatch_threads_num[1] != 1 || input.dispatch_threads_num[2] != 1))) { input.dispatch_threads_num[1] != 1 || input.dispatch_threads_num[2] != 1))) {
@@ -150,23 +138,17 @@ bool RenderExecutor::TryConsumeComputeImageClear(const ShaderComputeInputInfo& i
const auto& fill = resources.uniform_fill; const auto& fill = resources.uniform_fill;
auto& cache = command.GetContext().GetTextureCache(); auto& cache = command.GetContext().GetTextureCache();
if (fill.kind == ShaderRecompiler::IR::UniformFillKind::Image) { if (fill.kind == ShaderRecompiler::IR::UniformFillKind::Image) {
if (mode != 0x41u || input.dispatch_thread_dimensions || fill.words != 1 || fill.value > 255 || if (mode != 0x41u || input.dispatch_thread_dimensions || fill.value > 255 ||
resources.images.size() != 1 || program.info.images.size() != 1 || fill.resource != 0 || input.threads_num[2] != 1)
resources.images[0].dword_count != 8 || input.threads_num[2] != 1 || return false;
fill.group_stride[2] != 1) return false;
const auto descriptor = DecodeNativeDescriptor<ShaderTextureResource>(resources.images[0]); const auto descriptor = DecodeNativeDescriptor<ShaderTextureResource>(resources.images[0]);
const auto& resource = program.info.images[0]; const auto& resource = program.info.images[0];
if (descriptor.IsNull() || descriptor.Format() != Prospero::BufferFormat::k8UInt || if (descriptor.IsNull() || descriptor.Format() != Prospero::BufferFormat::k8UInt ||
descriptor.Type() != Prospero::ImageType::kColor2DArray || descriptor.Type() != Prospero::ImageType::kColor2DArray || descriptor.MetaCompress() ||
descriptor.MetaCompress() || descriptor.WriteCompress() || descriptor.WriteCompress() || descriptor.BaseLevel() > descriptor.LastLevel() ||
descriptor.BaseLevel() > descriptor.LastLevel() ||
descriptor.BaseLevel() > descriptor.MaxMip() || descriptor.BaseLevel() > descriptor.MaxMip() ||
descriptor.BaseArray5() > descriptor.Depth() || descriptor.BaseArray5() > descriptor.Depth() || descriptor.DstSelX() != 4)
descriptor.DstSelX() != 4 || (resource.shader_swizzle & 7u) != 4 || return false;
resource.numeric_class != Prospero::TextureNumericClass::Uint ||
resource.dimension != ShaderRecompiler::Decoder::ImageDimension::Dim2DArray ||
resource.read || resource.atomic || !resource.written ||
resource.mip_mode != ShaderRecompiler::IR::ImageMipMode::None) return false;
const std::array extents { const std::array extents {
std::max(1u, (descriptor.Width5() + 1u) >> descriptor.BaseLevel()), std::max(1u, (descriptor.Width5() + 1u) >> descriptor.BaseLevel()),
std::max(1u, (descriptor.Height5() + 1u) >> descriptor.BaseLevel()), std::max(1u, (descriptor.Height5() + 1u) >> descriptor.BaseLevel()),
@@ -202,7 +184,7 @@ bool RenderExecutor::TryConsumeComputeImageClear(const ShaderComputeInputInfo& i
ShaderBufferResource descriptor; ShaderBufferResource descriptor;
uint32_t packed_clear = 0; uint32_t packed_clear = 0;
uint64_t size = 0; uint64_t size = 0;
if (!ResolveComputeImageClear(input, group_x, group_y, group_z, mode, descriptor, packed_clear, if (!ResolveComputeBufferFill(input, group_x, group_y, group_z, mode, descriptor, packed_clear,
size)) { size)) {
return false; return false;
} }
@@ -219,10 +201,7 @@ bool RenderExecutor::TryConsumeComputeImageClear(const ShaderComputeInputInfo& i
registered_metadata ? "tracked" : "deferred", input.stage.program->shader_hash, registered_metadata ? "tracked" : "deferred", input.stage.program->shader_hash,
descriptor.Base48(), size, packed_clear); descriptor.Base48(), size, packed_clear);
} }
if (registered_metadata) { return registered_metadata;
return true;
}
return false;
} }
static std::atomic<uint32_t> logged_clears {0}; static std::atomic<uint32_t> logged_clears {0};
if (logged_clears.fetch_add(1, std::memory_order_relaxed) < 32) { if (logged_clears.fetch_add(1, std::memory_order_relaxed) < 32) {
+60 -68
View File
@@ -5398,11 +5398,15 @@ public:
const auto count = copy.imageExtent.width * copy.imageExtent.height; const auto count = copy.imageExtent.width * copy.imageExtent.height;
const auto *bytes = stencil_bytes + copy.bufferOffset; const auto *bytes = stencil_bytes + copy.bufferOffset;
const bool depth = sub.aspectMask == vk::ImageAspectFlagBits::eDepth; const bool depth = sub.aspectMask == vk::ImageAspectFlagBits::eDepth;
uint32_t expected = 0x91u;
if (depth) {
expected = std::bit_cast<uint32_t>(0.75f);
} else if (sub.mipLevel == 1 && sub.baseArrayLayer == 1) {
expected = 0x35u;
}
for (uint32_t pixel = 0; pixel < count; pixel++) { for (uint32_t pixel = 0; pixel < count; pixel++) {
uint32_t actual = bytes[pixel]; uint32_t actual = bytes[pixel];
if (depth) std::memcpy(&actual, bytes + pixel * 4, 4); if (depth) std::memcpy(&actual, bytes + pixel * 4, 4);
const uint32_t expected = depth ? std::bit_cast<uint32_t>(0.75f)
: sub.mipLevel == 1 && sub.baseArrayLayer == 1 ? 0x35u : 0x91u;
Require(name, "stencil clear preserves depth and neighbors", actual == expected, Require(name, "stencil clear preserves depth and neighbors", actual == expected,
"selected stencil clear changed depth, another mip, or another layer"); "selected stencil clear changed depth, another mip, or another layer");
} }
@@ -22760,29 +22764,18 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
auto result = ShaderRecompiler::CompileProgram( auto result = ShaderRecompiler::CompileProgram(
std::move(translated), options, specialization); std::move(translated), options, specialization);
ValidateSpirv("Ps5GameExampleImageClear", result.spirv); ValidateSpirv("Ps5GameExampleImageClear", result.spirv);
return CompiledShader{std::move(result.spirv), std::move(result.program), return resources;
std::move(resources), {}};
}; };
const auto code = MakeCode(); const auto code = MakeCode();
auto positive = Compile(code); auto positive = Compile(code);
ShaderRecompiler::IR::CompiledShaderInfo program{}; compute.stage.resources = positive;
program.stage = positive.program.stage;
program.shader_hash = positive.program.shader_hash;
program.wave_size = positive.program.wave_size;
program.user_data_base = positive.program.user_data_base;
program.user_data_count = positive.program.user_data_count;
program.scratch_dwords = positive.program.scratch_dwords;
program.info = std::move(positive.program.info);
program.bindings = std::move(positive.program.bindings);
compute.stage.program = &program;
compute.stage.resources = positive.resources;
ShaderBufferResource descriptor{}; ShaderBufferResource descriptor{};
u32 packed_clear = 0; u32 packed_clear = 0;
uint64_t size = 0; uint64_t size = 0;
Require("Ps5GameExampleImageClear", "runtime shape", Require("Ps5GameExampleImageClear", "runtime shape",
ResolveComputeImageClear(compute, 64, 1, 1, 0x61u, descriptor, ResolveComputeBufferFill(compute, 64, 1, 1, 0x61u, descriptor,
packed_clear, size) && packed_clear, size) &&
descriptor.Base48() == 0x10000u && size == 64u * 16u && descriptor.Base48() == 0x10000u && size == 64u * 16u &&
packed_clear == 0xff000000u, packed_clear == 0xff000000u,
@@ -22790,22 +22783,22 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
auto wrong_index = code; auto wrong_index = code;
wrong_index[0] = (wrong_index[0] & ~(0x3ffu << 16u)) | (0x347u << 16u); wrong_index[0] = (wrong_index[0] & ~(0x3ffu << 16u)) | (0x347u << 16u);
compute.stage.resources = Compile(wrong_index).resources; compute.stage.resources = Compile(wrong_index);
Require("Ps5GameExampleImageClear", "add-before-shift address", Require("Ps5GameExampleImageClear", "add-before-shift address",
!ResolveComputeImageClear(compute, 64, 1, 1, 0x61u, descriptor, !ResolveComputeBufferFill(compute, 64, 1, 1, 0x61u, descriptor,
packed_clear, size), packed_clear, size),
"V_ADD_LSHL was mistaken for V_LSHL_ADD contiguous coverage"); "V_ADD_LSHL was mistaken for V_LSHL_ADD contiguous coverage");
user_data[7] ^= 1u; user_data[7] ^= 1u;
compute.stage.resources = Compile(code).resources; compute.stage.resources = Compile(code);
Require("Ps5GameExampleImageClear", "non-repeated clear", Require("Ps5GameExampleImageClear", "non-repeated clear",
!ResolveComputeImageClear(compute, 64, 1, 1, 0x61u, descriptor, !ResolveComputeBufferFill(compute, 64, 1, 1, 0x61u, descriptor,
packed_clear, size), packed_clear, size),
"non-uniform uint4 data was replaced with a color clear"); "non-uniform uint4 data was replaced with a color clear");
compute.stage.resources = positive.resources; compute.stage.resources = positive;
compute.dispatch_threads_num[0] = 32; compute.dispatch_threads_num[0] = 32;
Require("Ps5GameExampleImageClear", "partial dispatch", Require("Ps5GameExampleImageClear", "partial dispatch",
!ResolveComputeImageClear(compute, 32, 1, 1, 0x61u, descriptor, !ResolveComputeBufferFill(compute, 32, 1, 1, 0x61u, descriptor,
packed_clear, size), packed_clear, size),
"partial buffer coverage was classified as a complete clear"); "partial buffer coverage was classified as a complete clear");
@@ -22821,46 +22814,43 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
scalar_code.push_back(EncodeMubuf1(1, 0, 0)); scalar_code.push_back(EncodeMubuf1(1, 0, 0));
AppendEnd(&scalar_code); AppendEnd(&scalar_code);
auto scalar = Compile(scalar_code); auto scalar = Compile(scalar_code);
program.info = scalar.program.info; compute.stage.resources = scalar;
compute.stage.resources = scalar.resources;
Require("Ps5GameExampleImageClear", "inline scalar fill", Require("Ps5GameExampleImageClear", "inline scalar fill",
ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size) && packed_clear, size) &&
descriptor.Base48() == 0x200000u && size == 256u && descriptor.Base48() == 0x200000u && size == 256u &&
packed_clear == 0u, packed_clear == 0u,
"GTA3's actual scalar store was not recognized from IR"); "GTA3's actual scalar store was not recognized from IR");
Require("Ps5GameExampleImageClear", "excess dispatch", Require("Ps5GameExampleImageClear", "excess dispatch",
!ResolveComputeImageClear(compute, 2, 1, 1, 0x41u, descriptor, !ResolveComputeBufferFill(compute, 2, 1, 1, 0x41u, descriptor,
packed_clear, size), packed_clear, size),
"excess invocation coverage was accepted as a fill"); "excess invocation coverage was accepted as a fill");
auto alias = scalar.resources.buffers.front(); auto alias = scalar.buffers.front();
compute.stage.resources.buffers.push_back(alias); compute.stage.resources.buffers.push_back(alias);
program.info.buffers.push_back({.read = true, .scalar = true});
Require("Ps5GameExampleImageClear", "aliased scalar input", Require("Ps5GameExampleImageClear", "aliased scalar input",
!ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, !ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size), packed_clear, size),
"a scalar read aliasing the destination was accepted as uniform"); "a scalar read aliasing the destination was accepted as uniform");
program.info = scalar.program.info; compute.stage.resources = scalar;
compute.stage.resources = scalar.resources;
compute.stage.resources.buffers[0].dwords[3] = compute.stage.resources.buffers[0].dwords[3] =
(static_cast<u32>(Prospero::BufferFormat::k32Float) << 12u) | 0x204u; (static_cast<u32>(Prospero::BufferFormat::k32Float) << 12u) | 0x204u;
Require("Ps5GameExampleImageClear", "wrong store format", Require("Ps5GameExampleImageClear", "wrong store format",
!ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, !ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size), packed_clear, size),
"a format that changes the stored value was accepted as a uint fill"); "a format that changes the stored value was accepted as a uint fill");
// A body change preserving every descriptor and dispatch field must change // A body change preserving every descriptor and dispatch field must change
// the result. // the result.
scalar_code[2] = EncodeVop1(0x01u, 1, InlineU32(7)); scalar_code[2] = EncodeVop1(0x01u, 1, InlineU32(7));
compute.stage.resources = Compile(scalar_code).resources; compute.stage.resources = Compile(scalar_code);
Require("Ps5GameExampleImageClear", "changed store value", Require("Ps5GameExampleImageClear", "changed store value",
ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size) && packed_clear, size) &&
packed_clear == 7u, packed_clear == 7u,
"clear recognition ignored the shader's actual stored value"); "clear recognition ignored the shader's actual stored value");
scalar_code[2] = EncodeVop1(0x01u, 1, Vgpr(0)); scalar_code[2] = EncodeVop1(0x01u, 1, Vgpr(0));
compute.stage.resources = Compile(scalar_code).resources; compute.stage.resources = Compile(scalar_code);
Require("Ps5GameExampleImageClear", "varying store value", Require("Ps5GameExampleImageClear", "varying store value",
!ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, !ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size), packed_clear, size),
"a varying store with identical resources was treated as a fill"); "a varying store with identical resources was treated as a fill");
@@ -22880,17 +22870,16 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
AppendEnd(&scalar_code); AppendEnd(&scalar_code);
clean_scalar = true; clean_scalar = true;
auto loaded = Compile(scalar_code); auto loaded = Compile(scalar_code);
program.info = loaded.program.info; compute.stage.resources = loaded;
compute.stage.resources = loaded.resources;
Require("Ps5GameExampleImageClear", "scalar-loaded fill", Require("Ps5GameExampleImageClear", "scalar-loaded fill",
ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size) && packed_clear, size) &&
packed_clear == scalar_clear && descriptor.Base48() == 0x200000u, packed_clear == scalar_clear && descriptor.Base48() == 0x200000u,
"GTA3's scalar input was confused with its destination"); "GTA3's scalar input was confused with its destination");
clean_scalar = false; clean_scalar = false;
compute.stage.resources = Compile(scalar_code).resources; compute.stage.resources = Compile(scalar_code);
Require("Ps5GameExampleImageClear", "unavailable clean scalar", Require("Ps5GameExampleImageClear", "unavailable clean scalar",
!ResolveComputeImageClear(compute, 1, 1, 1, 0x41u, descriptor, !ResolveComputeBufferFill(compute, 1, 1, 1, 0x41u, descriptor,
packed_clear, size), packed_clear, size),
"a scalar without a clean reader was replaced by a fill"); "a scalar without a clean reader was replaced by a fill");
std::printf("[host] %-32s ok\n", "Ps5GameExampleImageClear"); std::printf("[host] %-32s ok\n", "Ps5GameExampleImageClear");
@@ -23178,12 +23167,6 @@ void CheckRenderTargetFormatContract() {
(void)SelectSampledColorView(vk::Format::eR8G8B8A8Unorm, (void)SelectSampledColorView(vk::Format::eR8G8B8A8Unorm,
vk::Format::eR8G8B8A8Unorm, vk::Format::eR8G8B8A8Unorm,
DstSel(7, 6, 5, 3)); DstSel(7, 6, 5, 3));
} else if (std::strcmp(kind, "sampled-depth-format") == 0) {
(void)SelectSampledDepthView(vk::Format::eD24UnormS8Uint,
vk::Format::eR8Unorm, DstSel(4, 4, 4, 4));
} else if (std::strcmp(kind, "sampled-depth-swizzle") == 0) {
(void)SelectSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR32Sfloat, DstSel(4, 5, 6, 7));
} else if (std::strcmp(kind, "storage-incompatible-format") == 0) { } else if (std::strcmp(kind, "storage-incompatible-format") == 0) {
ValidateStorageColorView(vk::Format::eR8G8B8A8Srgb, ValidateStorageColorView(vk::Format::eR8G8B8A8Srgb,
vk::Format::eR16G16B16A16Unorm, vk::Format::eR16G16B16A16Unorm,
@@ -23397,34 +23380,44 @@ void CheckSampledColorViews() {
vk::Format::eR8G8B8A8Unorm, vk::Format::eR8G8B8A8Unorm,
DstSel(4, 5, 6, 7)) == DstSel(4, 5, 6, 7), DstSel(4, 5, 6, 7)) == DstSel(4, 5, 6, 7),
"compatible integer render-target sampled view was rejected"); "compatible integer render-target sampled view was rejected");
Require("SampledColorViews", "invalid depth format",
!IsSupportedSampledDepthView(vk::Format::eD24UnormS8Uint,
vk::Format::eR8Unorm,
DstSel(4, 4, 4, 4)),
"incompatible sampled depth format was accepted");
Require("SampledColorViews", "invalid depth swizzle",
!IsSupportedSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR32Sfloat,
DstSel(4, 5, 6, 7)),
"incompatible sampled depth swizzle was accepted");
Require("SampledColorViews", "D32 depth target", Require("SampledColorViews", "D32 depth target",
SelectSampledDepthView(vk::Format::eD32SfloatS8Uint, IsSupportedSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR32Sfloat, vk::Format::eR32Sfloat,
DstSel(4, 4, 4, 4)) == DstSel(4, 4, 4, 4), DstSel(4, 4, 4, 4)),
"D32 depth target did not select its depth-aspect view"); "D32 depth target did not select its depth-aspect view");
Require("SampledColorViews", "D16 R000 depth target", Require("SampledColorViews", "D16 R000 depth target",
SelectSampledDepthView(vk::Format::eD16Unorm, vk::Format::eR16Unorm, IsSupportedSampledDepthView(
DstSel(4, 0, 0, 0)) == DstSel(4, 0, 0, 0), vk::Format::eD16Unorm, vk::Format::eR16Unorm, DstSel(4, 0, 0, 0)),
"D16 depth target did not select its R000 depth-aspect view"); "D16 depth target did not select its R000 depth-aspect view");
Require("SampledColorViews", "D16S8 R001 depth target", Require("SampledColorViews", "D16S8 R001 depth target",
SelectSampledDepthView(vk::Format::eD16UnormS8Uint, IsSupportedSampledDepthView(vk::Format::eD16UnormS8Uint,
vk::Format::eR16Unorm, vk::Format::eR16Unorm,
DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1), DstSel(4, 0, 0, 1)),
"D16S8 depth target did not select its R001 depth-aspect view"); "D16S8 depth target did not select its R001 depth-aspect view");
Require("SampledColorViews", "promoted D24S8 R001 depth target", Require("SampledColorViews", "promoted D24S8 R001 depth target",
SelectSampledDepthView(vk::Format::eD24UnormS8Uint, IsSupportedSampledDepthView(vk::Format::eD24UnormS8Uint,
vk::Format::eR16Unorm, vk::Format::eR16Unorm,
DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1), DstSel(4, 0, 0, 1)),
"D24S8 host fallback did not preserve the guest R16 depth view"); "D24S8 host fallback did not preserve the guest R16 depth view");
Require("SampledColorViews", "promoted D32S8 R001 depth target", Require("SampledColorViews", "promoted D32S8 R001 depth target",
SelectSampledDepthView(vk::Format::eD32SfloatS8Uint, IsSupportedSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR16Unorm, vk::Format::eR16Unorm,
DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1), DstSel(4, 0, 0, 1)),
"D32S8 host fallback did not preserve the guest R16 depth view"); "D32S8 host fallback did not preserve the guest R16 depth view");
Require("SampledColorViews", "D32S8 R001 depth target", Require("SampledColorViews", "D32S8 R001 depth target",
SelectSampledDepthView(vk::Format::eD32SfloatS8Uint, IsSupportedSampledDepthView(vk::Format::eD32SfloatS8Uint,
vk::Format::eR32Sfloat, vk::Format::eR32Sfloat,
DstSel(4, 0, 0, 1)) == DstSel(4, 0, 0, 1), DstSel(4, 0, 0, 1)),
"D32S8 depth target did not select its R001 depth-aspect view"); "D32S8 depth target did not select its R001 depth-aspect view");
ShaderRecompiler::IR::ImageResource storage_resource{}; ShaderRecompiler::IR::ImageResource storage_resource{};
storage_resource.resource_class = storage_resource.resource_class =
@@ -23470,10 +23463,9 @@ void CheckSampledColorViews() {
"GetModuleFileName failed"); "GetModuleFileName failed");
for (const char *kind : for (const char *kind :
{"sampled-invalid-selector", "sampled-incompatible-format", {"sampled-invalid-selector", "sampled-incompatible-format",
"sampled-invalid-high", "sampled-depth-format", "sampled-depth-swizzle", "sampled-invalid-high", "storage-incompatible-format", "storage-kind",
"storage-incompatible-format", "storage-kind", "storage-no-write", "storage-no-write", "storage-nonuint-atomic", "storage-compare",
"storage-nonuint-atomic", "storage-compare", "storage-dimension", "storage-dimension", "volume-mip-count", "volume-slice-range"}) {
"volume-mip-count", "volume-slice-range"}) {
std::string command = std::string command =
std::string("\"") + path + "\" --image-view-death " + kind; std::string("\"") + path + "\" --image-view-death " + kind;
std::vector<char> mutable_command(command.begin(), command.end()); std::vector<char> mutable_command(command.begin(), command.end());