mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
Fix layered image subresource synchronization
This commit is contained in:
@@ -1001,7 +1001,7 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
|
||||
" size=0x%016" PRIx64 " read=%d written=%d formatted=%d\n",
|
||||
vaddr, size, is_read, is_written, is_formatted);
|
||||
}
|
||||
m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, true);
|
||||
(void)m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, true);
|
||||
}
|
||||
}
|
||||
if (is_written) {
|
||||
@@ -1421,6 +1421,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
|
||||
}
|
||||
ValidateGpuAccess(src_vaddr, size, true, false);
|
||||
ValidateGpuAccess(dst_vaddr, size, false, true);
|
||||
bool dst_image_transition = false;
|
||||
{
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
const bool src_image_gpu = m_texture_cache->HasGpuModifiedRangeOverlap(src_vaddr, size);
|
||||
@@ -1452,7 +1453,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
|
||||
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
src_vaddr, dst_vaddr, size);
|
||||
}
|
||||
m_texture_cache->InvalidateMemoryFromGPU(dst_vaddr, size);
|
||||
dst_image_transition = m_texture_cache->InvalidateMemoryFromGPU(dst_vaddr, size);
|
||||
// A clean target destination is handled above like a protected host write. Target
|
||||
// aliases that require an actual GPU buffer copy remain unsupported.
|
||||
if (m_texture_cache->HasGpuTargetPageOverlap(dst_vaddr, size)) {
|
||||
@@ -1470,7 +1471,8 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
|
||||
}
|
||||
}
|
||||
const auto [src, src_offset] = ObtainBuffer(command, ctx, src_vaddr, size, false, true);
|
||||
const auto [dst, dst_offset] = ObtainBuffer(command, ctx, dst_vaddr, size, true, false);
|
||||
const auto [dst, dst_offset] =
|
||||
ObtainBuffer(command, ctx, dst_vaddr, size, true, false, dst_image_transition);
|
||||
if (src == dst && src_offset < dst_offset + size && dst_offset < src_offset + size) {
|
||||
EXIT("BufferCache: resolved Vulkan copy ranges overlap, src_offset=0x%016" PRIx64
|
||||
" dst_offset=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
|
||||
@@ -330,6 +330,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index,
|
||||
render_target_slice_offset);
|
||||
}
|
||||
r->base_array_layer = view.base_layer;
|
||||
const uint32_t levels = rt.attrib2.num_mip_levels + 1u;
|
||||
if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) {
|
||||
EXIT("unsupported render-target mip range: current=%u levels=%u\n",
|
||||
|
||||
@@ -624,7 +624,7 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi
|
||||
}
|
||||
// For an equal-address allocation-pool entry, a changed block raster or block size is a new
|
||||
// image allocation, not a view of the old image. In Kyty we can only retire an
|
||||
// already-published target with page-isolated storage :)
|
||||
// already-published target with page-isolated storage.
|
||||
const bool page_isolated =
|
||||
cached.address % TRACKER_PAGE_SIZE == 0 && cached.size % TRACKER_PAGE_SIZE == 0 &&
|
||||
requested.address % TRACKER_PAGE_SIZE == 0 && requested.size % TRACKER_PAGE_SIZE == 0;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "libs/errno.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -1247,6 +1248,24 @@ void RenderDrawIndexAuto(uint64_t submit_id, CommandBuffer* buffer, HW::Context*
|
||||
index_bind, false, false, true);
|
||||
}
|
||||
|
||||
bool IsSameColorResolveSubresource(const RenderColorInfo& src, const RenderColorInfo& dst) {
|
||||
return src.base_addr == dst.base_addr && src.base_mip_level == dst.base_mip_level &&
|
||||
src.base_array_layer == dst.base_array_layer;
|
||||
}
|
||||
|
||||
ImageImageCopy MakeColorResolveCopy(const RenderColorInfo& src, const RenderColorInfo& dst,
|
||||
uint32_t width, uint32_t height) {
|
||||
ImageImageCopy region {};
|
||||
region.src_image = src.vulkan_buffer;
|
||||
region.src_level = src.base_mip_level;
|
||||
region.dst_level = dst.base_mip_level;
|
||||
region.width = width;
|
||||
region.height = height;
|
||||
region.src_layer = src.base_array_layer;
|
||||
region.dst_layer = dst.base_array_layer;
|
||||
return region;
|
||||
}
|
||||
|
||||
static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const HW::Context& hw,
|
||||
uint32_t render_target_slice_offset) {
|
||||
if (hw.GetColorControl().mode != 3) {
|
||||
@@ -1268,7 +1287,7 @@ static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const
|
||||
src.type == RenderColorType::NoColorOutput || dst.type == RenderColorType::NoColorOutput) {
|
||||
return false;
|
||||
}
|
||||
if (src.base_addr == dst.base_addr && src.base_mip_level == dst.base_mip_level) {
|
||||
if (IsSameColorResolveSubresource(src, dst)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1278,14 +1297,7 @@ static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ImageImageCopy> regions;
|
||||
ImageImageCopy region {};
|
||||
region.src_image = src.vulkan_buffer;
|
||||
region.src_level = src.base_mip_level;
|
||||
region.dst_level = dst.base_mip_level;
|
||||
region.width = width;
|
||||
region.height = height;
|
||||
regions.push_back(region);
|
||||
const std::array regions {MakeColorResolveCopy(src, dst, width, height)};
|
||||
|
||||
MarkRenderTargetGpuWritten(dst);
|
||||
UtilImageToImage(buffer, regions, dst.vulkan_buffer, dst.vulkan_buffer->layout);
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
struct DepthStencilVulkanImage;
|
||||
struct ImageImageCopy;
|
||||
struct RenderTextureVulkanImage;
|
||||
struct ShaderVertexInputInfo;
|
||||
struct VulkanImage;
|
||||
@@ -236,6 +237,7 @@ struct RenderColorInfo {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent = {};
|
||||
uint32_t base_mip_level = 0;
|
||||
uint32_t base_array_layer = 0;
|
||||
uint64_t base_addr = 0;
|
||||
uint64_t buffer_size = 0;
|
||||
uint32_t target_slot = 0;
|
||||
@@ -243,6 +245,12 @@ struct RenderColorInfo {
|
||||
VkClearColorValue color_clear_value {};
|
||||
};
|
||||
|
||||
[[nodiscard]] bool IsSameColorResolveSubresource(const RenderColorInfo& src,
|
||||
const RenderColorInfo& dst);
|
||||
[[nodiscard]] ImageImageCopy MakeColorResolveCopy(const RenderColorInfo& src,
|
||||
const RenderColorInfo& dst, uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
struct ScissorRect {
|
||||
int left = 0;
|
||||
int top = 0;
|
||||
|
||||
@@ -387,18 +387,9 @@ struct TextureCache::ReadbackWorker {
|
||||
}
|
||||
download.resize(info.size);
|
||||
std::fill(download.begin(), download.end(), 0);
|
||||
std::vector<ImageBufferCopy> regions;
|
||||
regions.reserve(info.layers);
|
||||
for (uint32_t layer = 0; layer < info.layers; layer++) {
|
||||
ImageBufferCopy region {};
|
||||
region.offset = static_cast<uint32_t>(slice_size * layer);
|
||||
region.pitch = info.pitch;
|
||||
region.width = info.width;
|
||||
region.height = info.height;
|
||||
region.src_layer = layer;
|
||||
region.aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
regions.push_back(region);
|
||||
}
|
||||
const auto regions =
|
||||
MakeLayeredImageBufferCopies(info.layers, slice_size, info.pitch, info.width,
|
||||
info.height, VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
UtilFillBuffer(cached.ctx, download.data(), info.size, regions, cached.image,
|
||||
cached.image->layout);
|
||||
guest.resize(info.size);
|
||||
@@ -467,17 +458,8 @@ struct TextureCache::ReadbackWorker {
|
||||
}
|
||||
download.resize(info.size);
|
||||
std::fill(download.begin(), download.end(), 0);
|
||||
std::vector<ImageBufferCopy> regions;
|
||||
regions.reserve(layers);
|
||||
for (uint32_t layer = 0; layer < layers; layer++) {
|
||||
ImageBufferCopy region {};
|
||||
region.offset = static_cast<uint32_t>(slice_size * layer);
|
||||
region.pitch = info.pitch;
|
||||
region.width = info.width;
|
||||
region.height = info.height;
|
||||
region.src_layer = layer;
|
||||
regions.push_back(region);
|
||||
}
|
||||
const auto regions =
|
||||
MakeLayeredImageBufferCopies(layers, slice_size, info.pitch, info.width, info.height);
|
||||
UtilFillBuffer(cached.ctx, download.data(), info.size, regions, cached.image,
|
||||
cached.image->layout);
|
||||
if (tiled) {
|
||||
@@ -3046,7 +3028,6 @@ void TextureCache::MarkGpuWritten(VulkanImage* image) {
|
||||
cached->depth.htile_address, cached->depth.htile_size, true,
|
||||
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
|
||||
meta->second.gpu_modified = true;
|
||||
meta->second.clear_mask = 0;
|
||||
}
|
||||
}
|
||||
if (!cached->gpu_modified) {
|
||||
@@ -3110,29 +3091,34 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) {
|
||||
target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
const auto rows = static_cast<uint64_t>(target.height - 1);
|
||||
TileSizeAlign exact {};
|
||||
const bool exact_tiled = tiled &&
|
||||
TileGetRenderTargetSize(target.width, target.height, target.pitch,
|
||||
target.bytes_per_element, &exact) &&
|
||||
exact.align == 65536 && exact.size == target.size;
|
||||
const bool single_slice = TileGetRenderTargetSize(target.width, target.height, target.pitch,
|
||||
target.bytes_per_element, &exact);
|
||||
const bool layered_size = target.layers != 0 && single_slice &&
|
||||
exact.size <= UINT64_MAX / target.layers &&
|
||||
exact.size * target.layers == target.size;
|
||||
const bool exact_tiled = tiled && exact.align == 65536 && layered_size;
|
||||
if (cached.kind != CachedImage::Kind::RenderTarget || !cached.gpu_modified ||
|
||||
cached.buffer_modified || cached.ctx == nullptr || cached.image == nullptr ||
|
||||
(!linear && !exact_tiled) || target.address == 0 || target.size == 0 || target.width == 0 ||
|
||||
target.height == 0 || target.pitch < target.width || target.bytes_per_element == 0 ||
|
||||
target.levels != 1 || rows > (UINT64_MAX - target.width) / target.pitch) {
|
||||
target.layers == 0 || target.size % target.layers != 0 || target.size > UINT32_MAX ||
|
||||
target.levels != 1 || cached.image->layers != target.layers ||
|
||||
rows > (UINT64_MAX - target.width) / target.pitch) {
|
||||
EXIT("TextureCache: unsupported render-target buffer synchronization, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" extent=%ux%u pitch=%u bpe=%u levels=%u tile=%u"
|
||||
" extent=%ux%u pitch=%u bpe=%u levels=%u layers=%u/%u tile=%u"
|
||||
" gpu_modified=%d buffer_modified=%d\n",
|
||||
target.address, target.size, target.width, target.height, target.pitch,
|
||||
target.bytes_per_element, target.levels, target.tile_mode, cached.gpu_modified,
|
||||
cached.buffer_modified);
|
||||
target.bytes_per_element, target.levels, target.layers, cached.image->layers,
|
||||
target.tile_mode, cached.gpu_modified, cached.buffer_modified);
|
||||
}
|
||||
const auto linear_elements = rows * target.pitch + target.width;
|
||||
if (linear_elements > UINT64_MAX / target.bytes_per_element) {
|
||||
EXIT("TextureCache: render-target buffer synchronization size overflow\n");
|
||||
}
|
||||
const auto linear_size = linear_elements * target.bytes_per_element;
|
||||
if (linear_size > target.size || cached.image->format != target.format ||
|
||||
const auto slice_size = target.size / target.layers;
|
||||
if (linear_size > slice_size || cached.image->format != target.format ||
|
||||
cached.image->extent.width != target.width ||
|
||||
cached.image->extent.height != target.height ||
|
||||
(tiled && target.bytes_per_element != 1 && target.bytes_per_element != 2 &&
|
||||
@@ -3149,7 +3135,9 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) {
|
||||
VulkanDeviceWaitIdle(cached.ctx);
|
||||
m_buffer_transition_linear.resize(target.size);
|
||||
std::fill(m_buffer_transition_linear.begin(), m_buffer_transition_linear.end(), 0);
|
||||
UtilFillBuffer(cached.ctx, m_buffer_transition_linear.data(), linear_size, target.pitch,
|
||||
const auto regions = MakeLayeredImageBufferCopies(target.layers, slice_size, target.pitch,
|
||||
target.width, target.height);
|
||||
UtilFillBuffer(cached.ctx, m_buffer_transition_linear.data(), target.size, regions,
|
||||
cached.image, cached.image->layout);
|
||||
if (tiled) {
|
||||
m_buffer_transition_guest.resize(target.size);
|
||||
@@ -3171,7 +3159,7 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) {
|
||||
cached.buffer_modified = true;
|
||||
}
|
||||
|
||||
void TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size,
|
||||
bool TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size,
|
||||
bool formatted_buffer_write) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
@@ -3226,23 +3214,24 @@ void TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size,
|
||||
sampled_write = sampled;
|
||||
}
|
||||
if (match == m_images.end()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (sampled_write) {
|
||||
(**match).buffer_modified = true;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (action == BufferImageAlias::VideoOutWrite) {
|
||||
auto& cached = **match;
|
||||
cached.buffer_modified = true;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (action == BufferImageAlias::RenderTargetWrite) {
|
||||
SynchronizeRenderTargetToBufferLocked(**match);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
m_memory_tracker.UntrackMemory(vaddr, size);
|
||||
m_images.erase(match);
|
||||
return true;
|
||||
}
|
||||
|
||||
DepthStencilVulkanImage* TextureCache::FindDepthTargetByRange(uint64_t vaddr, uint64_t size) {
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
uint64_t size, uint32_t packed_clear);
|
||||
void MarkGpuWritten(VulkanImage* image);
|
||||
void PrepareHostWrite(uint64_t vaddr, uint64_t size);
|
||||
void InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size,
|
||||
[[nodiscard]] bool InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size,
|
||||
bool formatted_buffer_write = false);
|
||||
[[nodiscard]] RenderTextureVulkanImage* FindRenderTargetByRange(CommandBuffer* command,
|
||||
uint64_t vaddr, uint64_t size);
|
||||
|
||||
@@ -22,6 +22,27 @@ static Common::Mutex g_scratch_buffer_mutex;
|
||||
static std::unique_ptr<uint8_t[]> g_scratch_buffer;
|
||||
static uint64_t g_scratch_buffer_capacity = 0;
|
||||
|
||||
std::vector<ImageBufferCopy> MakeLayeredImageBufferCopies(uint32_t layers, uint64_t slice_size,
|
||||
uint32_t pitch, uint32_t width,
|
||||
uint32_t height,
|
||||
VkImageAspectFlags aspect) {
|
||||
if (layers == 0 || slice_size == 0 || slice_size > UINT32_MAX ||
|
||||
slice_size > UINT32_MAX / layers || pitch < width || width == 0 || height == 0) {
|
||||
EXIT("invalid layered image-buffer copy layout\n");
|
||||
}
|
||||
std::vector<ImageBufferCopy> regions(layers);
|
||||
for (uint32_t layer = 0; layer < layers; layer++) {
|
||||
auto& region = regions[layer];
|
||||
region.offset = static_cast<uint32_t>(slice_size * layer);
|
||||
region.pitch = pitch;
|
||||
region.width = width;
|
||||
region.height = height;
|
||||
region.src_layer = layer;
|
||||
region.aspect = aspect;
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
UtilScratchBuffer::UtilScratchBuffer(uint64_t size) {
|
||||
EXIT_IF(size == 0);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -64,6 +65,11 @@ struct ImageImageCopy {
|
||||
int dst_z = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<ImageBufferCopy>
|
||||
MakeLayeredImageBufferCopies(uint32_t layers, uint64_t slice_size, uint32_t pitch, uint32_t width,
|
||||
uint32_t height,
|
||||
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
|
||||
enum class StagingBufferType { Texture, Vertex, ReadBack };
|
||||
|
||||
class UtilScratchBuffer {
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderState.h"
|
||||
#include "graphics/host_gpu/renderer/resourceMutex.h"
|
||||
#include "graphics/host_gpu/renderer/tiler.h"
|
||||
#include "graphics/host_gpu/renderer/textureCache.h"
|
||||
#include "graphics/host_gpu/utils.h"
|
||||
#include "graphics/shader/recompiler/BindingLayout.h"
|
||||
#include "graphics/shader/recompiler/ShaderDecoder.h"
|
||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||
@@ -9485,6 +9487,31 @@ void CheckStorageTextureVolumeUploadLayout() {
|
||||
std::printf("[host] %-32s ok\n", "StorageTextureVolumeUpload");
|
||||
}
|
||||
|
||||
void CheckColorResolveLayers() {
|
||||
RenderColorInfo src{};
|
||||
RenderColorInfo dst{};
|
||||
src.base_addr = dst.base_addr = 0x12340000;
|
||||
src.base_mip_level = dst.base_mip_level = 2;
|
||||
src.base_array_layer = 1;
|
||||
dst.base_array_layer = 3;
|
||||
src.vulkan_buffer = reinterpret_cast<VulkanImage *>(uintptr_t{1});
|
||||
|
||||
Require("ColorResolveLayers", "identity",
|
||||
!IsSameColorResolveSubresource(src, dst),
|
||||
"different array layers were treated as the same resolve subresource");
|
||||
const auto copy = MakeColorResolveCopy(src, dst, 128, 64);
|
||||
Require("ColorResolveLayers", "region",
|
||||
copy.src_image == src.vulkan_buffer && copy.src_level == 2 &&
|
||||
copy.dst_level == 2 && copy.src_layer == 1 &&
|
||||
copy.dst_layer == 3 && copy.width == 128 && copy.height == 64,
|
||||
"color resolve dropped its source or destination array layer");
|
||||
dst.base_array_layer = src.base_array_layer;
|
||||
Require("ColorResolveLayers", "same subresource",
|
||||
IsSameColorResolveSubresource(src, dst),
|
||||
"matching color resolve subresources were not recognized");
|
||||
std::printf("[host] %-32s ok\n", "ColorResolveLayers");
|
||||
}
|
||||
|
||||
void CheckRenderTargetTileRoundTrip() {
|
||||
struct Case {
|
||||
uint32_t width;
|
||||
@@ -9529,6 +9556,64 @@ void CheckRenderTargetTileRoundTrip() {
|
||||
"linear-to-tiled render-target conversion did not round-trip");
|
||||
}
|
||||
}
|
||||
constexpr uint32_t width = 257;
|
||||
constexpr uint32_t height = 131;
|
||||
constexpr uint32_t bytes_per_element = 4;
|
||||
constexpr uint32_t layers = 3;
|
||||
const auto pitch = TileGetRenderTargetPitch(width, bytes_per_element);
|
||||
TileSizeAlign storage{};
|
||||
Require("RenderTargetTileRoundTrip", "layered layout",
|
||||
TileGetRenderTargetSize(width, height, pitch, bytes_per_element,
|
||||
&storage) &&
|
||||
storage.size <= UINT32_MAX / layers,
|
||||
"layered render-target test layout was rejected");
|
||||
RenderTargetInfo info{};
|
||||
info.address = 1;
|
||||
info.size = storage.size * layers;
|
||||
info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.pitch = pitch;
|
||||
info.bytes_per_element = bytes_per_element;
|
||||
info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
info.layers = layers;
|
||||
std::vector<uint8_t> linear(info.size, 0);
|
||||
for (uint32_t layer = 0; layer < layers; layer++) {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
const auto row = storage.size * layer +
|
||||
static_cast<uint64_t>(y) * pitch * bytes_per_element;
|
||||
std::fill_n(linear.data() + row, width * bytes_per_element,
|
||||
static_cast<uint8_t>(0x31 + layer * 0x27));
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> tiled(info.size, 0xcd);
|
||||
std::vector<uint8_t> restored(info.size, 0xa5);
|
||||
Tiler tiler;
|
||||
tiler.TileImage(tiled.data(), linear.data(), info);
|
||||
for (uint32_t layer = 0; layer < layers; layer++) {
|
||||
TileConvertTiledToLinearRenderTarget(
|
||||
restored.data() + storage.size * layer,
|
||||
tiled.data() + storage.size * layer, width, height, pitch,
|
||||
bytes_per_element, storage.size);
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
const auto row = storage.size * layer +
|
||||
static_cast<uint64_t>(y) * pitch * bytes_per_element;
|
||||
Require("RenderTargetTileRoundTrip", "layered contents",
|
||||
std::memcmp(linear.data() + row, restored.data() + row,
|
||||
width * bytes_per_element) == 0,
|
||||
"layered render-target conversion crossed array slices");
|
||||
}
|
||||
}
|
||||
const auto regions = MakeLayeredImageBufferCopies(
|
||||
layers, storage.size, pitch, width, height);
|
||||
Require("RenderTargetTileRoundTrip", "layered regions",
|
||||
regions.size() == layers && regions[0].offset == 0 &&
|
||||
regions[1].offset == storage.size &&
|
||||
regions[2].offset == storage.size * 2 &&
|
||||
regions[0].src_layer == 0 && regions[1].src_layer == 1 &&
|
||||
regions[2].src_layer == 2 && regions[2].pitch == pitch &&
|
||||
regions[2].width == width && regions[2].height == height,
|
||||
"layered image-buffer regions did not preserve slice offsets");
|
||||
std::printf("[host] %-32s ok\n", "RenderTargetTileRoundTrip");
|
||||
}
|
||||
|
||||
@@ -10083,7 +10168,8 @@ void CheckOverlappingMetadataViews() {
|
||||
void CheckGpuMetadataReuse() {
|
||||
constexpr uintptr_t base = 0x0000000200010000ull;
|
||||
constexpr uint64_t allocation_size = 0x20000;
|
||||
constexpr uint64_t metadata_size = 0x8000;
|
||||
constexpr uint64_t metadata_size = 0x18000;
|
||||
constexpr uint32_t layers = 3;
|
||||
auto *memory = static_cast<uint8_t *>(VirtualAlloc(
|
||||
reinterpret_cast<void *>(base), allocation_size,
|
||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
||||
@@ -10100,22 +10186,34 @@ void CheckGpuMetadataReuse() {
|
||||
buffer_cache.SetTextureCache(texture_cache);
|
||||
page_manager.OnGpuMap(base, allocation_size);
|
||||
|
||||
texture_cache.RegisterMeta(base, metadata_size);
|
||||
texture_cache.RegisterMeta(base, metadata_size, layers);
|
||||
Require("GpuMetadataReuse", "clear", texture_cache.ClearMeta(base),
|
||||
"metadata clear setup failed");
|
||||
texture_cache.InvalidateMemoryFromGPU(base, allocation_size);
|
||||
const bool full_image_transition =
|
||||
texture_cache.InvalidateMemoryFromGPU(base, allocation_size);
|
||||
Require("GpuMetadataReuse", "discard",
|
||||
!texture_cache.IsMeta(base) &&
|
||||
!full_image_transition && !texture_cache.IsMeta(base) &&
|
||||
!texture_cache.HasMetaRangeOverlap(base, allocation_size),
|
||||
"fully overwritten metadata identity was retained");
|
||||
texture_cache.RegisterMeta(base, metadata_size);
|
||||
"metadata-only overwrite retained identity or claimed an image transition");
|
||||
texture_cache.RegisterMeta(base, metadata_size, layers);
|
||||
Require("GpuMetadataReuse", "re-register",
|
||||
texture_cache.IsMetaRange(base, metadata_size) &&
|
||||
texture_cache.ClearMeta(base),
|
||||
"metadata identity could not be reused after a GPU overwrite");
|
||||
texture_cache.InvalidateMemoryFromGPU(base + 0x1000, 0x1000);
|
||||
Require("GpuMetadataReuse", "partial discard", !texture_cache.IsMeta(base),
|
||||
"partially overwritten metadata identity was retained");
|
||||
Require("GpuMetadataReuse", "layered clear",
|
||||
texture_cache.IsMetaCleared(base, 0) &&
|
||||
texture_cache.IsMetaCleared(base, 1) &&
|
||||
texture_cache.IsMetaCleared(base, 2) &&
|
||||
texture_cache.TouchMeta(base, 1, false) &&
|
||||
texture_cache.IsMetaCleared(base, 0) &&
|
||||
!texture_cache.IsMetaCleared(base, 1) &&
|
||||
texture_cache.IsMetaCleared(base, 2),
|
||||
"consuming one metadata slice erased another slice's lazy clear");
|
||||
const bool partial_image_transition =
|
||||
texture_cache.InvalidateMemoryFromGPU(base + 0x1000, 0x1000);
|
||||
Require("GpuMetadataReuse", "partial discard",
|
||||
!partial_image_transition && !texture_cache.IsMeta(base),
|
||||
"partial metadata overwrite retained identity or claimed an image transition");
|
||||
|
||||
texture_cache.UnmapMemory(base, allocation_size);
|
||||
page_manager.OnGpuUnmap(base, allocation_size);
|
||||
@@ -11379,6 +11477,13 @@ int main(int argc, char **argv) {
|
||||
CheckOverlappingMetadataViews();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) {
|
||||
CheckColorResolveLayers();
|
||||
CheckRenderTargetTileRoundTrip();
|
||||
CheckDepthTargetTileRoundTrip();
|
||||
CheckGpuMetadataReuse();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) {
|
||||
CheckSampledColorViews();
|
||||
return 0;
|
||||
@@ -11445,6 +11550,7 @@ int main(int argc, char **argv) {
|
||||
CheckStorageTextureDepthTileUploadLayout();
|
||||
CheckStorageTextureLinearReadbackLayout();
|
||||
CheckStorageImageSwizzleSpecializationId();
|
||||
CheckColorResolveLayers();
|
||||
CheckRenderTargetTileRoundTrip();
|
||||
CheckDepthTargetTileRoundTrip();
|
||||
CheckStencilTargetTileRoundTrip();
|
||||
|
||||
Reference in New Issue
Block a user