mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
graphics: preserve stencil view layers through uploads and clears
This commit is contained in:
+42
-10
@@ -1037,6 +1037,8 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
|
||||
auto info = destination.info;
|
||||
if (image.depth_id) {
|
||||
info.data = image.info.data;
|
||||
info.resources = {image.stencil_subresources.level_count,
|
||||
image.stencil_subresources.layer_count};
|
||||
info.guest_format = Prospero::BufferFormat::k8UInt;
|
||||
info.bytes_per_block = 1;
|
||||
if (info.IsTiled()) info.pitch = TileGetDepthPitch(info.extent.width, 1, 0);
|
||||
@@ -1051,6 +1053,11 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
|
||||
auto copies = BuildDepthCopies(info, full_slice_size, image.depth_id
|
||||
? vk::ImageAspectFlagBits::eStencil
|
||||
: vk::ImageAspectFlagBits::eDepth);
|
||||
if (image.depth_id) {
|
||||
for (auto& copy: copies) {
|
||||
copy.imageSubresource.baseArrayLayer += image.stencil_subresources.base_layer;
|
||||
}
|
||||
}
|
||||
TileManager::Result linear {source.Handle(), source_offset, source.Size() - source_offset};
|
||||
if (info.IsTiled()) {
|
||||
const auto tiles = BuildDepthTiles(info);
|
||||
@@ -1239,7 +1246,8 @@ ImageId TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) {
|
||||
}
|
||||
auto& record = m_slot_images[association];
|
||||
TouchImage(record);
|
||||
record.depth_id = depth_id;
|
||||
record.depth_id = depth_id;
|
||||
record.stencil_subresources = depth.stencil_subresources;
|
||||
return association;
|
||||
}
|
||||
|
||||
@@ -1445,6 +1453,18 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
TouchImage(image);
|
||||
image.MarkGpuModified();
|
||||
image.usage.depth_target = true;
|
||||
if (desc.info.HasStencil()) {
|
||||
const auto layers = image.info.resources.layers;
|
||||
EXIT_IF(layers == 0 || image.info.data.size % layers != 0 ||
|
||||
desc.info.data.address < image.info.data.address);
|
||||
const auto slice_size = image.info.data.size / layers;
|
||||
const auto offset = desc.info.data.address - image.info.data.address;
|
||||
EXIT_IF(slice_size == 0 || offset % slice_size != 0 || offset / slice_size >= layers ||
|
||||
desc.info.resources.layers > layers - offset / slice_size);
|
||||
image.stencil_subresources = {0, desc.info.resources.levels,
|
||||
static_cast<uint32_t>(offset / slice_size),
|
||||
desc.info.resources.layers};
|
||||
}
|
||||
image.info.stencil = desc.info.stencil;
|
||||
image.info.metadata = desc.info.metadata;
|
||||
if (desc.info.HasMetadata()) {
|
||||
@@ -1491,9 +1511,11 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
||||
if (command.IsInvalid() || !GuestRange {address, size}.Valid()) {
|
||||
EXIT("TextureCache: invalid image clear\n");
|
||||
}
|
||||
std::scoped_lock lock {m_lock};
|
||||
ImageId selected {};
|
||||
vk::ImageAspectFlags aspect {};
|
||||
std::scoped_lock lock {m_lock};
|
||||
ImageId selected {};
|
||||
ImageId stencil_id {};
|
||||
vk::ImageAspectFlags aspect {};
|
||||
ImageSubresourceRange subresources;
|
||||
for (const auto id: FindImagesInRegion(address, size, false)) {
|
||||
auto owner = m_slot_images.try_get(id);
|
||||
if (owner == nullptr) {
|
||||
@@ -1501,10 +1523,13 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
||||
}
|
||||
vk::ImageAspectFlags candidate {};
|
||||
ImageId candidate_id = id;
|
||||
ImageSubresourceRange candidate_subresources {
|
||||
0, owner->info.resources.levels, 0, owner->info.TransferLayers()};
|
||||
if (owner->depth_id && owner->info.data.address == address &&
|
||||
owner->info.data.size == size) {
|
||||
candidate = vk::ImageAspectFlagBits::eStencil;
|
||||
candidate_id = owner->depth_id;
|
||||
candidate = vk::ImageAspectFlagBits::eStencil;
|
||||
candidate_id = owner->depth_id;
|
||||
candidate_subresources = owner->stencil_subresources;
|
||||
owner = m_slot_images.try_get(candidate_id);
|
||||
if (owner == nullptr || owner->backing.image == nullptr || !owner->info.HasStencil()) {
|
||||
continue;
|
||||
@@ -1517,11 +1542,13 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
if (selected && selected != candidate_id) {
|
||||
if (selected && (selected != candidate_id || subresources != candidate_subresources)) {
|
||||
return false;
|
||||
}
|
||||
selected = candidate_id;
|
||||
aspect = candidate;
|
||||
selected = candidate_id;
|
||||
stencil_id = candidate == vk::ImageAspectFlagBits::eStencil ? id : ImageId {};
|
||||
aspect = candidate;
|
||||
subresources = candidate_subresources;
|
||||
}
|
||||
if (!selected) {
|
||||
return false;
|
||||
@@ -1543,7 +1570,12 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
||||
clear.depthStencil.stencil = stencil_clear;
|
||||
}
|
||||
ClearImage(command, selected, image.backing.format,
|
||||
{aspect, 0, image.info.resources.levels, 0, image.info.TransferLayers()}, clear);
|
||||
{aspect, subresources.base_level, subresources.level_count,
|
||||
subresources.base_layer, subresources.layer_count}, clear);
|
||||
if (stencil_id) {
|
||||
TrackImage(stencil_id);
|
||||
CommitGpuWrite(m_slot_images[stencil_id]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -673,7 +673,9 @@ Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
} // namespace ImageOps
|
||||
|
||||
Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageInfo& image_info)
|
||||
: info(image_info), m_graphics(graphics), m_scheduler(scheduler) {
|
||||
: info(image_info),
|
||||
stencil_subresources {0, image_info.resources.levels, 0, image_info.resources.layers},
|
||||
m_graphics(graphics), m_scheduler(scheduler) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
ImageOps::Validate(info);
|
||||
m_cpu_dirty =
|
||||
|
||||
@@ -148,6 +148,8 @@ public:
|
||||
uint64_t track_addr = 0;
|
||||
uint64_t track_addr_end = 0;
|
||||
ImageId depth_id {};
|
||||
// The current stencil plane's mapping into the depth image, also retained by its association.
|
||||
ImageSubresourceRange stencil_subresources;
|
||||
uint64_t tick_accessed_last = 0;
|
||||
size_t lru_id = 0;
|
||||
|
||||
|
||||
@@ -6007,6 +6007,101 @@ public:
|
||||
"sampling");
|
||||
DestroyBuffer(&layered_raw_d16_readback);
|
||||
|
||||
auto layered_stencil_desc = MakeLinearDesc(
|
||||
base + 0x27d4000, 6 * 4 * sizeof(float),
|
||||
vk::Format::eD32SfloatS8Uint, Prospero::BufferFormat::k32Float,
|
||||
Prospero::ImageType::kColor2D, {4, 1, 1}, 6, 4, 1);
|
||||
layered_stencil_desc.type = BindingType::DepthTarget;
|
||||
// Four live stencil bytes per slice, with allocation padding.
|
||||
constexpr uint32_t stencil_slice_size = 16;
|
||||
layered_stencil_desc.info.stencil = {base + 0x27d6000, 6 * stencil_slice_size};
|
||||
layered_stencil_desc.view_info.aspect =
|
||||
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||
layered_stencil_desc.view_info.usage =
|
||||
vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
const auto layered_stencil_id = texture_cache.FindImage(layered_stencil_desc);
|
||||
auto &layered_stencil_native = texture_cache.GetImage(layered_stencil_id);
|
||||
layered_stencil_native.Transit(vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::AccessFlagBits2::eTransferWrite, {}, scheduler.Current().Handle());
|
||||
const vk::ClearDepthStencilValue layered_stencil_clear{0.75f, 0x91};
|
||||
const vk::ImageSubresourceRange layered_stencil_range{
|
||||
layered_stencil_desc.view_info.aspect, 0, 1, 0, 6};
|
||||
scheduler.Current().Handle().clearDepthStencilImage(
|
||||
layered_stencil_native.backing.image,
|
||||
vk::ImageLayout::eTransferDstOptimal, &layered_stencil_clear, 1,
|
||||
&layered_stencil_range);
|
||||
texture_cache.MarkGpuWritten(layered_stencil_id);
|
||||
std::array<uint32_t, 6> stencil_expected;
|
||||
stencil_expected.fill(0x91919191u);
|
||||
constexpr std::array<std::array<uint32_t, 3>, 4> stencil_cases{{
|
||||
{0, 1, 0x35353535u}, {0, 3, 0x57575757u},
|
||||
{2, 1, 0x79797979u}, {0, 1, 0xa5a5a5a5u}}};
|
||||
for (uint32_t pass = 0; pass < stencil_cases.size(); pass++) {
|
||||
const auto [first, layers, stencil_word] = stencil_cases[pass];
|
||||
auto partial_stencil_desc = layered_stencil_desc;
|
||||
partial_stencil_desc.info.data.address += first * 4 * sizeof(float);
|
||||
partial_stencil_desc.info.data.size = layers * 4 * sizeof(float);
|
||||
partial_stencil_desc.info.stencil.address += first * stencil_slice_size;
|
||||
partial_stencil_desc.info.stencil.size = layers * stencil_slice_size;
|
||||
partial_stencil_desc.info.resources.layers = layers;
|
||||
partial_stencil_desc.info.mip_layout[0].size =
|
||||
partial_stencil_desc.info.data.size;
|
||||
partial_stencil_desc.view_info.type = layers == 1
|
||||
? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
partial_stencil_desc.view_info.layer_count = layers;
|
||||
const auto stencil = partial_stencil_desc.info.stencil;
|
||||
if (pass != 3) {
|
||||
(void)resources.GetBufferCache().ObtainBuffer(stencil.address, stencil.size, true);
|
||||
resources.GetBufferCache().FillBuffer(
|
||||
stencil.address, stencil.size, stencil_word, false);
|
||||
const auto partial_stencil_id = texture_cache.FindImage(partial_stencil_desc);
|
||||
Require(name, "partial stencil retains depth owner",
|
||||
partial_stencil_id == layered_stencil_id &&
|
||||
texture_cache.GetImage(partial_stencil_id).info.resources.layers == 6 &&
|
||||
partial_stencil_desc.view_info.base_layer == first &&
|
||||
texture_cache.FindDepthTarget(partial_stencil_id,
|
||||
partial_stencil_desc) != nullptr,
|
||||
"a partial stencil view replaced its six-layer depth owner");
|
||||
} else {
|
||||
// The old prefix association must survive the owner's shifted binding.
|
||||
Require(name, "partial stencil buffer clear",
|
||||
texture_cache.ClearImageFromBuffer(scheduler.Current(),
|
||||
stencil.address, stencil.size, stencil_word),
|
||||
"the existing partial stencil association could not be cleared");
|
||||
const auto rebound = texture_cache.FindImage(partial_stencil_desc);
|
||||
Require(name, "rebind cleared stencil association",
|
||||
rebound == layered_stencil_id &&
|
||||
texture_cache.FindDepthTarget(rebound, partial_stencil_desc) != nullptr,
|
||||
"the cleared stencil association lost its depth owner");
|
||||
}
|
||||
std::fill_n(stencil_expected.begin() + first, layers, stencil_word);
|
||||
auto readback = CreateHostBuffer(name, 30 * sizeof(uint32_t),
|
||||
vk::BufferUsageFlagBits::eTransferDst, {});
|
||||
std::vector<vk::BufferImageCopy> copies(2);
|
||||
copies[0].imageSubresource = {vk::ImageAspectFlagBits::eDepth, 0, 0, 6};
|
||||
copies[1].bufferOffset = 24 * sizeof(float);
|
||||
copies[1].imageSubresource = {vk::ImageAspectFlagBits::eStencil, 0, 0, 6};
|
||||
for (auto © : copies) copy.imageExtent = {4, 1, 1};
|
||||
texture_cache.GetImage(layered_stencil_id).Download(
|
||||
copies, readback.buffer, 0, readback.size);
|
||||
HostReadBarrier(readback.buffer, readback.size,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
scheduler.Finish();
|
||||
const auto words = ReadBuffer(name, readback, 30);
|
||||
for (uint32_t layer = 0; layer < 6; layer++) {
|
||||
for (uint32_t pixel = 0; pixel < 4; pixel++) {
|
||||
Require(name, "partial stencil preserves depth",
|
||||
words[layer * 4 + pixel] == std::bit_cast<uint32_t>(0.75f),
|
||||
"stencil upload changed the retained depth plane");
|
||||
}
|
||||
Require(name, "partial stencil preserves other layers",
|
||||
words[24 + layer] == stencil_expected[layer],
|
||||
"stencil upload used the retained depth allocation's layer count");
|
||||
}
|
||||
DestroyBuffer(&readback);
|
||||
}
|
||||
|
||||
auto native_array_info = array_desc.info;
|
||||
native_array_info.data = {};
|
||||
auto native_volume_info = volume_desc.info;
|
||||
|
||||
Reference in New Issue
Block a user