Keep reused BC5 textures in their own guest layout

This commit is contained in:
BlackCAT authored and nmzik committed 2026-10-03 14:19:32 +04:00
1 parent ef4c5baf3a
commit 0d4f993358
4 files changed
+116 -46

No files matched your search

+11 -23
View File
@@ -791,13 +791,6 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
if (const auto depth_id = ResolveDepthOverlap(requested, binding, cached_id)) {
return {depth_id};
}
if (requested.IsBlock() && !cached.info.IsBlock()) {
return {ExpandImage(requested, cached_id)};
}
if (requested.data.size == cached.info.data.size &&
(requested.IsVolume() || cached.info.IsVolume())) {
return {ExpandImage(requested, cached_id)};
}
// Equal pitch does not imply equal mip placement: a changed extent can move
// a level into or out of the mip tail. These are separate guest layouts.
if (requested.tile_mode != cached.info.tile_mode ||
@@ -808,6 +801,13 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
}
return {merged_id};
}
if (requested.IsBlock() && !cached.info.IsBlock()) {
return {ExpandImage(requested, cached_id)};
}
if (requested.data.size == cached.info.data.size &&
(requested.IsVolume() || cached.info.IsVolume())) {
return {ExpandImage(requested, cached_id)};
}
// PPSA08394
// A view cannot change the native image type or grow its extent.
if (requested.data.size == cached.info.data.size &&
@@ -878,9 +878,7 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId source_id) {
RefreshCopySource(source_id);
const auto expanded_id = InsertImage(info);
auto& expanded = m_slot_images[expanded_id];
auto& source = m_slot_images[source_id];
expanded.usage = source.usage;
if (source.binding.is_bound || source.binding.is_target) {
source.binding.needs_rebind = true;
}
@@ -926,22 +924,12 @@ TextureCache::BuildTextureTransfer(const Image& image, BindingType binding,
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;
uint32_t layers = info.TransferLayers();
bool volume = info.IsVolume();
bool allow_depth_tile = upload;
const char* owner = "TextureCache readback";
TextureTransfer transfer;
transfer.swap_bgra16 = info.bgra16 && (!upload || render_target || video_out);
if (render_target) {
format = ImageOps::RenderTargetTransferFormat(info.bytes_per_block);
}
if (video_out) {
allow_depth_tile = false;
} else if (render_target || binding == BindingType::Storage) {
allow_depth_tile = true;
}
transfer.swap_bgra16 = info.bgra16;
if (upload) {
if ((render_target || video_out) &&
(info.resources.layers == 0 || info.data.size % info.resources.layers != 0 ||
@@ -963,9 +951,9 @@ TextureCache::BuildTextureTransfer(const Image& image, BindingType binding,
}
}
transfer.layout = TextureCalcUploadLayout(format, info.extent.width, info.extent.height,
info.resources.levels, layers, info.tile_mode,
info.data.size, allow_depth_tile, volume, owner);
transfer.layout = TextureCalcUploadLayout(info.guest_format, info.extent.width,
info.extent.height, info.resources.levels, layers,
info.tile_mode, info.data.size, volume, owner);
transfer.regions = TextureBuildImageCopies(transfer.layout);
if (info.IsDepth()) {
for (auto& region: transfer.regions) {
@@ -175,8 +175,7 @@ SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
uint32_t height, uint32_t levels, uint32_t depth,
Prospero::TileMode tile_mode, uint64_t upload_size,
bool allow_depth_tile, bool volume_texture,
const char* owner) {
bool volume_texture, const char* owner) {
TextureUploadLayout layout {};
layout.surface.description = {
format,
@@ -204,8 +203,7 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
static_cast<uint32_t>(format));
}
} else {
if ((tile_mode == Prospero::TileMode::kDepth && !allow_depth_tile) ||
!TileGetTiledTextureLayout(description, layout.surface)) {
if (!TileGetTiledTextureLayout(description, layout.surface)) {
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
owner, static_cast<uint32_t>(format), static_cast<uint32_t>(tile_mode),
@@ -47,8 +47,7 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
uint32_t height, uint32_t levels, uint32_t depth,
Prospero::TileMode tile_mode, uint64_t upload_size,
bool allow_depth_tile, bool volume_texture,
const char* owner);
bool volume_texture, const char* owner);
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t levels,
+102 -17
View File
@@ -5437,6 +5437,91 @@ public:
previous = id;
}
// The game reuses a raw render target allocation for a larger BC5 mip
// chain. Equal block extents do not make their guest tiling compatible.
constexpr uint64_t bc5_alias_offset = 0x1800000;
TileSurfaceLayout bc5_surface{};
Require(name, "BC5 alias layout",
TileGetTiledTextureLayout(
{.format = Prospero::BufferFormat::kBc5UNorm,
.tile_mode = Prospero::TileMode::kStandard64KB,
.width = 1024, .height = 1024, .levels = 11}, bc5_surface) &&
bc5_surface.total_size == 0x160000,
"captured BC5 allocation no longer has its 0x160000-byte footprint");
std::memset(memory + bc5_alias_offset, 0, bc5_surface.total_size);
auto raw_alias = MakeLinearDesc(
base + bc5_alias_offset, 0x100000, vk::Format::eR32G32B32A32Sfloat,
Prospero::BufferFormat::k32_32_32_32Float, Prospero::ImageType::kColor2D,
{256, 256, 1}, 1, 16, 1);
raw_alias.type = BindingType::RenderTarget;
raw_alias.info.tile_mode = Prospero::TileMode::kRenderTarget;
raw_alias.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
const auto raw_alias_id = texture_cache.FindImage(raw_alias);
(void)texture_cache.FindRenderTarget(raw_alias_id, raw_alias);
vk::ClearValue raw_alias_clear{};
std::fill_n(raw_alias_clear.color.float32.data(), 4, 1.0f);
TextureCacheTestAccess::ClearImage(
texture_cache, scheduler.Current(), raw_alias_id,
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, raw_alias_clear);
Require(name, "BC5 alias CPU overwrite",
resources.InvalidateMemory(base + bc5_alias_offset,
bc5_surface.total_size),
"new BC5 data could not invalidate the old render target");
std::vector<u32> bc5_guest(bc5_surface.total_size / sizeof(u32));
std::iota(bc5_guest.begin(), bc5_guest.end(), 0x41000000u);
std::memcpy(memory + bc5_alias_offset, bc5_guest.data(),
bc5_surface.total_size);
auto bc5_alias = MakeLinearDesc(
base + bc5_alias_offset, bc5_surface.total_size, vk::Format::eBc5UnormBlock,
Prospero::BufferFormat::kBc5UNorm, Prospero::ImageType::kColor2D,
{1024, 1024, 1}, 1, 16, 1);
bc5_alias.info.resources.levels = 11;
bc5_alias.info.tile_mode = Prospero::TileMode::kStandard64KB;
bc5_alias.view_info.level_count = 11;
std::vector<vk::BufferImageCopy> bc5_probes;
std::vector<u32> bc5_expected;
for (u32 level = 0; level < 11; ++level) {
const auto &layout = bc5_surface.mips[level];
bc5_alias.info.mip_layout[level] = {
layout.offset, layout.size, layout.padded_width, layout.padded_height};
u32 offset = 0;
Require(name, "BC5 alias block address",
TileGetBlockOffset(bc5_surface.texture.block, layout.tail_x,
layout.tail_y, 0, offset),
"BC5 mip origin could not be addressed in guest memory");
vk::BufferImageCopy copy{};
copy.bufferOffset = bc5_expected.size() * sizeof(u32);
copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, level, 0, 1};
copy.imageExtent = {std::min(1024u >> level, 4u),
std::min(1024u >> level, 4u), 1};
bc5_probes.push_back(copy);
const auto word = (layout.offset + offset) / sizeof(u32);
bc5_expected.insert(bc5_expected.end(), bc5_guest.begin() + word,
bc5_guest.begin() + word + 4);
}
const auto bc5_alias_id = texture_cache.FindImage(bc5_alias);
Require(name, "BC5 alias owner separation",
bc5_alias_id != raw_alias_id &&
TextureCacheTestAccess::Contains(texture_cache, raw_alias_id) &&
!texture_cache.GetImage(bc5_alias_id).usage.render_target &&
texture_cache.GetImage(bc5_alias_id).info.tile_mode ==
Prospero::TileMode::kStandard64KB,
"BC5 image expanded the unrelated raw render target or inherited its usage");
(void)texture_cache.FindTexture(bc5_alias_id, bc5_alias);
auto bc5_readback = CreateHostBuffer(
name, bc5_expected.size() * sizeof(u32),
vk::BufferUsageFlagBits::eTransferDst, std::vector<u32>(bc5_expected.size()));
texture_cache.GetImage(bc5_alias_id).Download(
bc5_probes, bc5_readback.buffer, 0, bc5_readback.size);
HostReadBarrier(bc5_readback.buffer, bc5_readback.size,
vk::PipelineStageFlagBits::eTransfer,
vk::AccessFlagBits::eTransferWrite);
scheduler.Finish();
Require(name, "BC5 alias mip contents",
ReadBuffer(name, bc5_readback, bc5_expected.size()) == bc5_expected,
"raw render-target copying overwrote the BC5 guest mip layout");
DestroyBuffer(&bc5_readback);
// A formatted Buffer read must use the private
// Exercise the cache-native image-copy path. Use a request larger
// than the stream shortcut and poison guest backing
@@ -6838,7 +6923,7 @@ public:
std::memset(memory + layered_offset, 0, layered_guest_size);
const auto layered_layout = TextureCalcUploadLayout(
Prospero::BufferFormat::k32Float, 2, 2, 2, 2,
Prospero::TileMode::kLinear, layered_guest_size, true, false,
Prospero::TileMode::kLinear, layered_guest_size, false,
"UnifiedTextureCacheFlow");
const auto layered_upload_regions =
TextureBuildImageCopies(layered_layout);
@@ -15799,7 +15884,7 @@ public:
TileSizeAlign total{};
TileGetTextureSize(format, 128, 128, 1, tile, &total, nullptr, nullptr);
const auto layout = TextureCalcUploadLayout(
format, 128, 128, 1, 1, tile, total.size, false, false, name);
format, 128, 128, 1, 1, tile, total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
Require(name, "FMASK policy",
@@ -15835,7 +15920,7 @@ public:
const auto layout =
TextureCalcUploadLayout(format, width, height, 1, 1, mode.tile,
total.size, false, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
if (!TextureBuildGpuTileInfos(total.size, regions, layout, 1, infos)) {
@@ -15932,7 +16017,7 @@ public:
nullptr);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, 1, tile,
total.size, false, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
const bool built =
@@ -15959,7 +16044,7 @@ public:
total);
const auto layout =
TextureCalcUploadLayout(format, width, height, 1, layers, tile,
total.size, true, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
const bool built =
@@ -15984,7 +16069,7 @@ public:
nullptr, nullptr);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, 1, mode.tile,
total.size, false, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
Require(name, "odd mip infos",
@@ -16023,7 +16108,7 @@ public:
nullptr, nullptr);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, 1, mode.tile,
total.size, true, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
Require(name, "2D mip tail seam",
@@ -16049,7 +16134,7 @@ public:
total);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, depth, tile,
total.size, false, true, name);
total.size, true, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
const bool built =
@@ -16080,7 +16165,7 @@ public:
total);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, depth, tile,
total.size, false, true, name);
total.size, true, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
Require(name, "3D BC mip infos",
@@ -16102,7 +16187,7 @@ public:
nullptr);
const auto layout =
TextureCalcUploadLayout(format, width, height, levels, 1, tile,
total.size, false, false, name);
total.size, false, name);
const auto regions = TextureBuildImageCopies(layout);
Require(name, "linear BC native regions",
regions.size() == levels &&
@@ -16144,7 +16229,7 @@ public:
test.tile, true, total);
const auto layout =
TextureCalcUploadLayout(test.format, width, height, levels, depth,
test.tile, total.size, true, true, name);
test.tile, total.size, true, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
const bool built =
@@ -16203,7 +16288,7 @@ public:
true, total);
const auto layout =
TextureCalcUploadLayout(tail.format, width, height, levels, depth,
tile, total.size, false, true, name);
tile, total.size, true, name);
const auto regions = TextureBuildImageCopies(layout);
std::vector<GpuTileInfo> infos;
bool valid = total.size == 8192 &&
@@ -16325,7 +16410,7 @@ public:
volume_size);
const auto volume_layout = TextureCalcUploadLayout(
volume_format, volume_width, volume_height, volume_levels, volume_depth,
volume_tile, volume_size.size, true, true, name);
volume_tile, volume_size.size, true, name);
const auto volume_copies = TextureBuildImageCopies(volume_layout);
std::vector<u32> volume_source(volume_size.size / sizeof(u32), 0);
std::vector<std::pair<size_t, u32>> volume_probes;
@@ -34060,7 +34145,7 @@ void CheckStorageTextureLinearUploadLayout() {
TileGetTextureTotalSize(format, width, height, depth, 1, tile, false, total);
const auto layout =
TextureCalcUploadLayout(format, width, height, 1, depth, tile, total.size,
true, false, "StorageTextureLinearTest");
false, "StorageTextureLinearTest");
const auto regions = TextureBuildImageCopies(layout);
Require("StorageTextureLinearUpload", "layout",
pitch == width && total.size == 0x1fa4000 && total.align == 256 &&
@@ -34089,7 +34174,7 @@ void CheckStorageTextureDepthTileUploadLayout() {
TileGetTextureTotalSize(format, width, height, depth, 1, tile, false, total);
const auto layout =
TextureCalcUploadLayout(format, width, height, 1, depth, tile, total.size,
true, false, "StorageTextureDepthTileTest");
false, "StorageTextureDepthTileTest");
const auto regions = TextureBuildImageCopies(layout);
Require("StorageTextureDepthTileUpload", "PPSA14053 layout",
pitch == 256 && padded.width == 256 && padded.height == 256 &&
@@ -34415,7 +34500,7 @@ void CheckStorageTextureVolumeUploadLayout() {
Prospero::TileMode::kRenderTarget, true, total);
const auto layout = TextureCalcUploadLayout(
format, width, height, 1, depth, Prospero::TileMode::kRenderTarget,
total.size, true, true, "StorageTextureVolumeTest");
total.size, true, "StorageTextureVolumeTest");
const auto regions = TextureBuildImageCopies(layout);
Require(
"StorageTextureVolumeUpload", "layout",
@@ -34450,7 +34535,7 @@ void CheckStorageTextureVolumeMipRegions() {
TileGetTextureTotalSize(format, width, height, depth, levels, tile, true,
total);
const auto layout = TextureCalcUploadLayout(
format, width, height, levels, depth, tile, total.size, true, true,
format, width, height, levels, depth, tile, total.size, true,
"StorageTextureVolumeMipTest");
const auto copies = TextureBuildImageCopies(layout);