mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
graphics: preserve PS5 array mip contents during cache promotion
PPSA05975
This commit is contained in:
+27
-38
@@ -810,17 +810,10 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
|
|||||||
const bool safe_to_delete =
|
const bool safe_to_delete =
|
||||||
current_tick - std::min(current_tick, cached.tick_accessed_last) > NumFramesBeforeRemoval;
|
current_tick - std::min(current_tick, cached.tick_accessed_last) > NumFramesBeforeRemoval;
|
||||||
|
|
||||||
if (requested.data.address == cached.info.data.address) {
|
const uint32_t requested_block = requested.bytes_per_block * requested.samples;
|
||||||
const uint32_t requested_block = requested.bytes_per_block * requested.samples;
|
const uint32_t cached_block = cached.info.bytes_per_block * cached.info.samples;
|
||||||
const uint32_t cached_block = cached.info.bytes_per_block * cached.info.samples;
|
if (requested.data.address == cached.info.data.address &&
|
||||||
if (requested.BlockExtent() != cached.info.BlockExtent() ||
|
requested.BlockExtent() == cached.info.BlockExtent() && requested_block == cached_block) {
|
||||||
requested_block != cached_block) {
|
|
||||||
if (safe_to_delete) {
|
|
||||||
FreeImage(cached_id);
|
|
||||||
}
|
|
||||||
return {merged_id};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto depth_id = ResolveDepthOverlap(requested, binding, cached_id)) {
|
if (const auto depth_id = ResolveDepthOverlap(requested, binding, cached_id)) {
|
||||||
return {depth_id};
|
return {depth_id};
|
||||||
}
|
}
|
||||||
@@ -873,38 +866,27 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
|
|||||||
static_cast<uint32_t>(cached.info.tile_mode));
|
static_cast<uint32_t>(cached.info.tile_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requested.data.address > cached.info.data.address) {
|
const int32_t requested_mip = requested.MipOf(cached.info);
|
||||||
const int32_t mip = requested.MipOf(cached.info);
|
if (requested_mip >= 0) {
|
||||||
if (mip >= 0) {
|
const int32_t layer = requested.SliceOf(cached.info, requested_mip);
|
||||||
const int32_t layer = requested.SliceOf(cached.info, mip);
|
return {cached_id, requested_mip, layer};
|
||||||
if (layer >= 0) {
|
|
||||||
return {cached_id, mip, layer};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (safe_to_delete) {
|
|
||||||
FreeImage(cached_id);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int32_t mip = cached.info.MipOf(requested);
|
const int32_t mip = cached.info.MipOf(requested);
|
||||||
if (mip >= 0) {
|
if (mip >= 0) {
|
||||||
const int32_t layer = cached.info.SliceOf(requested, mip);
|
const int32_t layer = cached.info.SliceOf(requested, mip);
|
||||||
if (layer >= 0) {
|
if (!merged_id) {
|
||||||
if (cached.binding.is_target) {
|
return {ExpandImage(requested, cached_id)};
|
||||||
cached.binding.needs_rebind = true;
|
|
||||||
if (merged_id) {
|
|
||||||
m_slot_images[merged_id].binding.is_target = true;
|
|
||||||
}
|
|
||||||
FreeImage(cached_id);
|
|
||||||
return {merged_id};
|
|
||||||
}
|
|
||||||
if (merged_id) {
|
|
||||||
CopyImageMip(merged_id, cached_id, static_cast<uint32_t>(mip),
|
|
||||||
static_cast<uint32_t>(layer));
|
|
||||||
FreeImage(cached_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
cached.binding.needs_rebind |= cached.binding.is_bound || cached.binding.is_target;
|
||||||
|
m_slot_images[merged_id].binding.is_target |= cached.binding.is_target;
|
||||||
|
CopyImageMip(merged_id, cached_id, static_cast<uint32_t>(mip),
|
||||||
|
static_cast<uint32_t>(layer));
|
||||||
|
FreeImage(cached_id);
|
||||||
|
return {merged_id};
|
||||||
|
}
|
||||||
|
if (requested.data.address >= cached.info.data.address && safe_to_delete) {
|
||||||
|
FreeImage(cached_id);
|
||||||
}
|
}
|
||||||
return {merged_id};
|
return {merged_id};
|
||||||
}
|
}
|
||||||
@@ -919,7 +901,14 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId source_id) {
|
|||||||
source.binding.needs_rebind = true;
|
source.binding.needs_rebind = true;
|
||||||
}
|
}
|
||||||
InitializeImage(expanded_id);
|
InitializeImage(expanded_id);
|
||||||
CopyImage(expanded_id, source_id);
|
const int32_t mip = source.info.MipOf(info);
|
||||||
|
const int32_t layer = source.info.SliceOf(info, mip);
|
||||||
|
if (layer >= 0) {
|
||||||
|
CopyImageMip(expanded_id, source_id, static_cast<uint32_t>(mip),
|
||||||
|
static_cast<uint32_t>(layer));
|
||||||
|
} else {
|
||||||
|
CopyImage(expanded_id, source_id);
|
||||||
|
}
|
||||||
FreeImage(source_id);
|
FreeImage(source_id);
|
||||||
return expanded_id;
|
return expanded_id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,9 +94,24 @@ struct ImageInfo {
|
|||||||
bytes_per_block == other.bytes_per_block;
|
bytes_per_block == other.bytes_per_block;
|
||||||
}
|
}
|
||||||
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
|
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
|
||||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || resources.levels != 1 ||
|
if (container.resources.levels > container.mip_layout.size()) {
|
||||||
container.resources.layers == 0 ||
|
return -1;
|
||||||
container.resources.levels > container.mip_layout.size()) {
|
}
|
||||||
|
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
||||||
|
if (SliceOf(container, static_cast<int32_t>(level)) >= 0) {
|
||||||
|
return static_cast<int32_t>(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||||
|
if (!IsCompatible(container) || tile_mode != container.tile_mode || type != container.type ||
|
||||||
|
IsVolume() || resources.levels != 1 || resources.layers == 0 ||
|
||||||
|
container.resources.layers == 0 || mip < 0 ||
|
||||||
|
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||||
|
container.resources.levels > container.mip_layout.size() ||
|
||||||
|
!data.Valid() || !container.data.Valid() || data.address < container.data.address ||
|
||||||
|
data.End() > container.data.End()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (HasStencil() != container.HasStencil() ||
|
if (HasStencil() != container.HasStencil() ||
|
||||||
@@ -105,75 +120,36 @@ struct ImageInfo {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t mip = -1;
|
const auto level = static_cast<uint32_t>(mip);
|
||||||
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
const auto& layout = container.mip_layout[level];
|
||||||
const auto& layout = container.mip_layout[level];
|
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
extent.height != std::max(container.extent.height >> level, 1u) ||
|
||||||
container.data.address > UINT64_MAX - layout.offset) {
|
extent.depth != container.extent.depth || mip_layout[0].pitch != layout.pitch ||
|
||||||
continue;
|
mip_layout[0].height != layout.height || layout.size == 0 ||
|
||||||
}
|
layout.size % container.resources.layers != 0 ||
|
||||||
const auto mip_base = container.data.address + layout.offset;
|
container.data.size % container.resources.layers != 0 ||
|
||||||
const auto slice_size = layout.size / container.resources.layers;
|
data.size % resources.layers != 0) {
|
||||||
if (slice_size == 0 || mip_base > UINT64_MAX - layout.size) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto mip_end = mip_base + layout.size;
|
|
||||||
if (data.address >= mip_base && data.address < mip_end &&
|
|
||||||
(data.address - mip_base) % slice_size == 0) {
|
|
||||||
mip = static_cast<int32_t>(level);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mip < 0) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto level = static_cast<uint32_t>(mip);
|
// PS5 block slices contain the entire reversed mip chain.
|
||||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
const auto slice_stride = container.data.size / container.resources.layers;
|
||||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
const auto child_stride = data.size / resources.layers;
|
||||||
|
if (child_stride != layout.size / container.resources.layers ||
|
||||||
|
layout.offset >= slice_stride || child_stride > slice_stride - layout.offset ||
|
||||||
|
(resources.layers > 1 && child_stride != slice_stride)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
const auto mip_depth = std::max(container.extent.depth >> level, 1u);
|
const auto address_delta = data.address - container.data.address;
|
||||||
if (container.type == Prospero::ImageType::kColor3D &&
|
if (address_delta < layout.offset || (address_delta - layout.offset) % slice_stride != 0) {
|
||||||
type == Prospero::ImageType::kColor2D) {
|
|
||||||
if (resources.layers != mip_depth) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (type != container.type) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return mip;
|
const auto layer = (address_delta - layout.offset) / slice_stride;
|
||||||
}
|
if (layer > INT32_MAX || layer >= container.resources.layers ||
|
||||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
resources.layers > container.resources.layers - layer) {
|
||||||
if (!IsCompatible(container) || type != container.type || mip < 0 ||
|
|
||||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
|
||||||
container.resources.levels > container.mip_layout.size() ||
|
|
||||||
container.resources.layers == 0 || data.size == 0) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
const auto level = static_cast<uint32_t>(mip);
|
return static_cast<int32_t>(layer);
|
||||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
|
||||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const auto& layout = container.mip_layout[level];
|
|
||||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
|
||||||
container.data.address > UINT64_MAX - layout.offset) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const auto slice_size = layout.size / container.resources.layers;
|
|
||||||
if (slice_size == 0 || data.size % slice_size != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const auto mip_base = container.data.address + layout.offset;
|
|
||||||
if (data.address < mip_base) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const auto address_delta = data.address - mip_base;
|
|
||||||
if (address_delta % data.size != 0 || address_delta / data.size > INT32_MAX) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<int32_t>(address_delta / data.size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4738,24 +4738,27 @@ public:
|
|||||||
"instead of being discarded and recreated");
|
"instead of being discarded and recreated");
|
||||||
|
|
||||||
ImageInfo chain = sampled.info;
|
ImageInfo chain = sampled.info;
|
||||||
chain.data = {0x10000, 0x8000};
|
chain.data = {0x2394c1000, 0x384000};
|
||||||
chain.extent = {8, 8, 1};
|
chain.extent = {64, 64, 1};
|
||||||
chain.resources = {2, 4};
|
chain.resources = {7, 150};
|
||||||
chain.pitch = 8;
|
chain.pitch = 64;
|
||||||
chain.mip_layout[0] = {0, 0x4000, 8, 8};
|
chain.tile_mode = Prospero::TileMode::kStandard4KB;
|
||||||
chain.mip_layout[1] = {0x4000, 0x4000, 4, 4};
|
chain.mip_layout[0] = {0x2000, 0x258000, 64, 64};
|
||||||
|
chain.mip_layout[1] = {0x1000, 0x96000, 32, 32};
|
||||||
|
for (uint32_t level = 2; level < chain.resources.levels; ++level) {
|
||||||
|
chain.mip_layout[level] = {0, 0x96000, 32, 32};
|
||||||
|
}
|
||||||
ImageInfo subresource = chain;
|
ImageInfo subresource = chain;
|
||||||
subresource.data = {0x16000, 0x1000};
|
subresource.data = {0x2394c3000, 0x4000};
|
||||||
subresource.extent = {4, 4, 1};
|
|
||||||
subresource.resources = {1, 1};
|
subresource.resources = {1, 1};
|
||||||
subresource.pitch = 4;
|
subresource.mip_layout = {};
|
||||||
subresource.mip_layout[0] = {0, 0x1000, 4, 4};
|
subresource.mip_layout[0] = {0, 0x4000, 64, 64};
|
||||||
const auto mip = subresource.MipOf(chain);
|
const auto mip = subresource.MipOf(chain);
|
||||||
Require(name, "overlap resolution",
|
Require(name, "overlap resolution",
|
||||||
mip == 1 && subresource.SliceOf(chain, mip) == 2 &&
|
mip == 0 && subresource.SliceOf(chain, mip) == 0 &&
|
||||||
ImageRangeOverlaps(chain.data, subresource.data) &&
|
ImageRangeOverlaps(chain.data, subresource.data) &&
|
||||||
ImagePageRangesOverlap(chain.data, subresource.data),
|
ImagePageRangesOverlap(chain.data, subresource.data),
|
||||||
"normalized mip/slice overlap did not resolve");
|
"captured PS5 atlas mip/slice overlap did not resolve");
|
||||||
|
|
||||||
auto exact_desc = compatible_desc;
|
auto exact_desc = compatible_desc;
|
||||||
const auto exact = texture_cache.FindImage(exact_desc, true);
|
const auto exact = texture_cache.FindImage(exact_desc, true);
|
||||||
@@ -10933,20 +10936,23 @@ public:
|
|||||||
stencil_layout.size, false),
|
stencil_layout.size, false),
|
||||||
"depth prefetch performed final target acquisition");
|
"depth prefetch performed final target acquisition");
|
||||||
|
|
||||||
auto target_parent =
|
auto target_parent = make_target_desc(base, 1536, {4, 4, 1});
|
||||||
make_target_desc(base, target_mip_size * 2, {4, 4, 1});
|
|
||||||
target_parent.type = BindingType::RenderTarget;
|
target_parent.type = BindingType::RenderTarget;
|
||||||
target_parent.info.resources.levels = 2;
|
target_parent.info.resources.levels = 2;
|
||||||
target_parent.info.mip_layout[0] = {0, target_mip_size, 4, 4};
|
// PS5 linear rows align to 256 bytes; smaller mips precede larger ones.
|
||||||
target_parent.info.mip_layout[1] = {target_mip_size, target_mip_size, 2,
|
target_parent.info.pitch = 64;
|
||||||
2};
|
target_parent.info.mip_layout[0] = {512, 1024, 64, 4};
|
||||||
|
target_parent.info.mip_layout[1] = {0, 512, 64, 2};
|
||||||
target_parent.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
target_parent.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||||
auto target_base_subresource =
|
auto target_base_subresource =
|
||||||
make_target_desc(base, target_mip_size, {4, 4, 1});
|
make_target_desc(base + 512, 1024, {4, 4, 1});
|
||||||
|
target_base_subresource.info.pitch = 64;
|
||||||
|
target_base_subresource.info.mip_layout[0] = {0, 1024, 64, 4};
|
||||||
const auto target_base_subresource_id =
|
const auto target_base_subresource_id =
|
||||||
texture_cache.FindImage(target_base_subresource);
|
texture_cache.FindImage(target_base_subresource);
|
||||||
auto target_subresource =
|
auto target_subresource = make_target_desc(base, 512, {2, 2, 1});
|
||||||
make_target_desc(base + target_mip_size, target_mip_size, {2, 2, 1});
|
target_subresource.info.pitch = 64;
|
||||||
|
target_subresource.info.mip_layout[0] = {0, 512, 64, 2};
|
||||||
const auto target_subresource_id =
|
const auto target_subresource_id =
|
||||||
texture_cache.FindImage(target_subresource);
|
texture_cache.FindImage(target_subresource);
|
||||||
const auto *target_subresource_owner =
|
const auto *target_subresource_owner =
|
||||||
@@ -10961,6 +10967,15 @@ public:
|
|||||||
!TextureCacheTestAccess::PendingDownload(
|
!TextureCacheTestAccess::PendingDownload(
|
||||||
texture_cache, target_subresource_id),
|
texture_cache, target_subresource_id),
|
||||||
"FindImage claimed RenderExecutor-owned render-target state");
|
"FindImage claimed RenderExecutor-owned render-target state");
|
||||||
|
vk::ClearValue target_clear{};
|
||||||
|
target_clear.color.uint32[0] = 0x12345678;
|
||||||
|
TextureCacheTestAccess::ClearImage(
|
||||||
|
texture_cache, scheduler.Current(), target_base_subresource_id,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, target_clear);
|
||||||
|
target_clear.color.uint32[0] = 0x89abcdef;
|
||||||
|
TextureCacheTestAccess::ClearImage(
|
||||||
|
texture_cache, scheduler.Current(), target_subresource_id,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, target_clear);
|
||||||
RenderExecutorTestAccess::BindRenderTarget(executor,
|
RenderExecutorTestAccess::BindRenderTarget(executor,
|
||||||
target_subresource_id);
|
target_subresource_id);
|
||||||
Require(name, "target prefetch purity",
|
Require(name, "target prefetch purity",
|
||||||
@@ -10982,6 +10997,34 @@ public:
|
|||||||
target_parent_id),
|
target_parent_id),
|
||||||
"target overlap did not transfer target state to the merged "
|
"target overlap did not transfer target state to the merged "
|
||||||
"owner");
|
"owner");
|
||||||
|
auto target_readback = CreateHostBuffer(
|
||||||
|
name, 2 * sizeof(u32), vk::BufferUsageFlagBits::eTransferDst,
|
||||||
|
{0xaaaaaaaa, 0xaaaaaaaa});
|
||||||
|
const std::array target_probes{
|
||||||
|
vk::BufferImageCopy{0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
|
||||||
|
{},
|
||||||
|
{1, 1, 1}},
|
||||||
|
vk::BufferImageCopy{sizeof(u32),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 1, 0, 1},
|
||||||
|
{},
|
||||||
|
{1, 1, 1}}};
|
||||||
|
texture_cache.GetImage(target_parent_id)
|
||||||
|
.Download(target_probes, target_readback.buffer, 0,
|
||||||
|
target_readback.size);
|
||||||
|
const vk::MemoryBarrier2 target_host_barrier{
|
||||||
|
.srcStageMask = vk::PipelineStageFlagBits2::eCopy,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eHost,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eHostRead};
|
||||||
|
vk::DependencyInfo target_dependency{};
|
||||||
|
target_dependency.memoryBarrierCount = 1;
|
||||||
|
target_dependency.pMemoryBarriers = &target_host_barrier;
|
||||||
|
scheduler.Current().Handle().pipelineBarrier2(target_dependency);
|
||||||
RenderColorInfo rebound_color{};
|
RenderColorInfo rebound_color{};
|
||||||
rebound_color.desc = target_subresource;
|
rebound_color.desc = target_subresource;
|
||||||
rebound_color.image_id = target_subresource_id;
|
rebound_color.image_id = target_subresource_id;
|
||||||
@@ -10989,6 +11032,11 @@ public:
|
|||||||
rebound_depth.desc = depth;
|
rebound_depth.desc = depth;
|
||||||
rebound_depth.image_id = depth_id;
|
rebound_depth.image_id = depth_id;
|
||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
|
Require(name, "promoted target mip contents",
|
||||||
|
ReadBuffer(name, target_readback, 2) ==
|
||||||
|
std::vector<u32>{0x12345678, 0x89abcdef},
|
||||||
|
"promotion discarded GPU contents from a child mip");
|
||||||
|
DestroyBuffer(&target_readback);
|
||||||
Require(name, "deferred target slot erasure",
|
Require(name, "deferred target slot erasure",
|
||||||
TextureCacheTestAccess::Owner(texture_cache,
|
TextureCacheTestAccess::Owner(texture_cache,
|
||||||
target_subresource_id) == nullptr,
|
target_subresource_id) == nullptr,
|
||||||
|
|||||||
+70
-17
@@ -1183,37 +1183,90 @@ void TestNativeShaderResourceDependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TestNormalizedImageContracts() {
|
void TestNormalizedImageContracts() {
|
||||||
|
// Captured PS5 256x256 atlas: each layer is a 0x56000-byte mip chain.
|
||||||
ImageInfo container{};
|
ImageInfo container{};
|
||||||
container.data = {0x10000, 0x15000};
|
container.data = {0x241741000, 0x2198000};
|
||||||
container.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
container.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
||||||
container.guest_format = Prospero::BufferFormat::k8_8_8_8UNorm;
|
container.guest_format = Prospero::BufferFormat::k8_8_8_8UNorm;
|
||||||
container.type = Prospero::ImageType::kColor2D;
|
container.type = Prospero::ImageType::kColor2D;
|
||||||
container.extent = {64, 64, 1};
|
container.extent = {256, 256, 1};
|
||||||
container.resources = {3, 4};
|
container.resources = {9, 100};
|
||||||
container.pitch = 64;
|
container.pitch = 256;
|
||||||
container.bytes_per_block = 4;
|
container.bytes_per_block = 4;
|
||||||
container.samples = 1;
|
container.samples = 1;
|
||||||
container.tile_mode = Prospero::TileMode::kStandard64KB;
|
container.tile_mode = Prospero::TileMode::kStandard4KB;
|
||||||
container.mip_layout[0] = {0, 0x10000, 64, 64};
|
container.mip_layout[0] = {0x16000, 0x1900000, 256, 256};
|
||||||
container.mip_layout[1] = {0x10000, 0x4000, 32, 32};
|
container.mip_layout[1] = {0x6000, 0x640000, 128, 128};
|
||||||
container.mip_layout[2] = {0x14000, 0x1000, 16, 16};
|
container.mip_layout[2] = {0x2000, 0x190000, 64, 64};
|
||||||
|
container.mip_layout[3] = {0x1000, 0x64000, 32, 32};
|
||||||
|
for (uint32_t level = 4; level < container.resources.levels; ++level) {
|
||||||
|
container.mip_layout[level] = {0, 0x64000, 32, 32};
|
||||||
|
}
|
||||||
|
|
||||||
ImageInfo subresource = container;
|
ImageInfo subresource = container;
|
||||||
subresource.data = {0x22000, 0x1000};
|
subresource.data = {0x2417ad000, 0x40000};
|
||||||
subresource.extent = {32, 32, 1};
|
|
||||||
subresource.resources = {1, 1};
|
subresource.resources = {1, 1};
|
||||||
subresource.pitch = 32;
|
|
||||||
subresource.mip_layout = {};
|
subresource.mip_layout = {};
|
||||||
subresource.mip_layout[0] = {0, 0x1000, 32, 32};
|
subresource.mip_layout[0] = {0, 0x40000, 256, 256};
|
||||||
|
|
||||||
Check(container.BlockExtent() == vk::Extent2D{64, 64},
|
Check(container.BlockExtent() == vk::Extent2D{256, 256},
|
||||||
"normalized image block extent changed");
|
"normalized image block extent changed");
|
||||||
Check(subresource.IsCompatible(container),
|
Check(subresource.IsCompatible(container),
|
||||||
"normalized compatible image was rejected");
|
"normalized compatible image was rejected");
|
||||||
Check(subresource.MipOf(container) == 1,
|
Check(subresource.MipOf(container) == 0 &&
|
||||||
"normalized mip lookup missed a subresource");
|
subresource.SliceOf(container, 0) == 1,
|
||||||
Check(subresource.SliceOf(container, 1) == 2,
|
"PS5 mip-chain stride did not resolve the captured second array layer");
|
||||||
"normalized slice lookup missed a subresource");
|
|
||||||
|
auto smaller = subresource;
|
||||||
|
smaller.data = {0x241747000, 0x10000};
|
||||||
|
smaller.extent = {128, 128, 1};
|
||||||
|
smaller.pitch = 128;
|
||||||
|
smaller.mip_layout[0] = {0, 0x10000, 128, 128};
|
||||||
|
Check(smaller.MipOf(container) == 1 && smaller.SliceOf(container, 1) == 0,
|
||||||
|
"PS5 reversed mip ordering did not resolve the captured smaller mip");
|
||||||
|
|
||||||
|
auto boundary = subresource;
|
||||||
|
boundary.data.address = container.data.address + 99 * 0x56000 + 0x16000;
|
||||||
|
Check(boundary.SliceOf(container, 0) == 99,
|
||||||
|
"PS5 array lookup rejected the final complete layer");
|
||||||
|
boundary.data.address += 0x56000;
|
||||||
|
Check(boundary.MipOf(container) == -1,
|
||||||
|
"PS5 array lookup accepted a layer beyond the parent range");
|
||||||
|
boundary = subresource;
|
||||||
|
boundary.data.address += 0x1000;
|
||||||
|
Check(boundary.MipOf(container) == -1,
|
||||||
|
"PS5 array lookup accepted a misaligned layer address");
|
||||||
|
boundary = subresource;
|
||||||
|
boundary.resources.layers = 2;
|
||||||
|
boundary.data.size *= 2;
|
||||||
|
boundary.mip_layout[0].size *= 2;
|
||||||
|
Check(boundary.MipOf(container) == -1,
|
||||||
|
"PS5 array lookup accepted contiguous child layers with a different stride");
|
||||||
|
boundary = subresource;
|
||||||
|
boundary.mip_layout[0].pitch *= 2;
|
||||||
|
Check(boundary.MipOf(container) == -1,
|
||||||
|
"PS5 array lookup accepted a different storage pitch");
|
||||||
|
|
||||||
|
auto small_atlas = container;
|
||||||
|
small_atlas.data = {0x239001000, 0x64000};
|
||||||
|
small_atlas.extent = {32, 32, 1};
|
||||||
|
small_atlas.pitch = 32;
|
||||||
|
small_atlas.resources = {6, 50};
|
||||||
|
small_atlas.mip_layout[0] = {0x1000, 0x32000, 32, 32};
|
||||||
|
for (uint32_t level = 1; level < small_atlas.resources.levels; ++level) {
|
||||||
|
small_atlas.mip_layout[level] = {0, 0x32000, 32, 32};
|
||||||
|
}
|
||||||
|
auto small_child = subresource;
|
||||||
|
small_child.data = {0x23900e000, 0x1000};
|
||||||
|
small_child.extent = {32, 32, 1};
|
||||||
|
small_child.pitch = 32;
|
||||||
|
small_child.mip_layout[0] = {0, 0x1000, 32, 32};
|
||||||
|
Check(small_child.MipOf(small_atlas) == 0 &&
|
||||||
|
small_child.SliceOf(small_atlas, 0) == 6,
|
||||||
|
"PS5 array lookup doubled the captured small-atlas layer index");
|
||||||
|
small_child.data.address = 0x23900d000;
|
||||||
|
Check(small_child.MipOf(small_atlas) == -1,
|
||||||
|
"PS5 array lookup treated a packed tail block as the full-size mip");
|
||||||
|
|
||||||
auto incompatible = subresource;
|
auto incompatible = subresource;
|
||||||
incompatible.samples = 2;
|
incompatible.samples = 2;
|
||||||
|
|||||||
Reference in New Issue
Block a user