graphics: add native PS5 MSAA and safe image allocation reuse

This commit is contained in:
nmzik
2026-07-21 07:37:52 +02:00
parent bd9086e484
commit 999f87576e
29 changed files with 1685 additions and 603 deletions
+83 -25
View File
@@ -11,6 +11,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <bit>
#include <cstring> #include <cstring>
#include <fmt/format.h> #include <fmt/format.h>
#include <vector> #include <vector>
@@ -329,6 +330,30 @@ static bool Gen5Thin64KBBlockSizeFromElementBytes(uint32_t bytes_per_element, ui
return false; return false;
} }
static bool Gen5Msaa64KBBlockSizeFromElementBytes(uint32_t bytes_per_element,
uint32_t num_fragments_log2,
uint32_t* block_width, uint32_t* block_height) {
if (num_fragments_log2 == 0) {
return Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, block_width, block_height);
}
if (num_fragments_log2 > 3 || !std::has_single_bit(bytes_per_element) ||
bytes_per_element > 16) {
return false;
}
// AGC kLog2BlockSizeMsaa. Row zero is the ordinary thin 64 KiB layout; rows 1..3
// describe 2x, 4x and 8x depth/render-target blocks respectively.
static constexpr uint8_t LOG2_BLOCK[4][5][2] = {
{{8, 8}, {8, 7}, {7, 7}, {7, 6}, {6, 6}},
{{7, 8}, {7, 7}, {6, 7}, {6, 6}, {5, 6}},
{{7, 7}, {7, 6}, {6, 6}, {6, 5}, {5, 5}},
{{6, 7}, {6, 6}, {5, 6}, {5, 5}, {4, 5}},
};
const auto bytes_log2 = std::countr_zero(bytes_per_element);
*block_width = 1u << LOG2_BLOCK[num_fragments_log2][bytes_log2][0];
*block_height = 1u << LOG2_BLOCK[num_fragments_log2][bytes_log2][1];
return true;
}
struct Gen5MipTailLocation { struct Gen5MipTailLocation {
uint32_t x; uint32_t x;
uint32_t y; uint32_t y;
@@ -2262,35 +2287,59 @@ void TileConvertLinearToTiledRenderTarget(void* dst, const void* src, uint32_t w
} }
} }
bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign* htile_size) {
*htile_size = {};
if (width == 0 || width > 16384 || height == 0 || height > 16384) {
return false;
}
// Prospero HTile stores one DWORD per depth tile. Its 32 KiB allocation blocks cover
// 1024x512 pixels, independently of the attachment's fragment count.
const uint64_t size = static_cast<uint64_t>(AlignUp(width, 1024u) / 1024u) *
(AlignUp(height, 512u) / 512u) * 32768u;
if (size == 0 || size > UINT32_MAX) {
return false;
}
*htile_size = {static_cast<uint32_t>(size), 32768};
return true;
}
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format, bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
uint32_t stencil_format, bool htile, TileSizeAlign* stencil_size, uint32_t stencil_format, bool htile, TileSizeAlign* stencil_size,
TileSizeAlign* htile_size, TileSizeAlign* depth_size) { TileSizeAlign* htile_size, TileSizeAlign* depth_size,
uint32_t num_fragments_log2) {
EXIT_IF(pitch != 0); EXIT_IF(pitch != 0);
// Prospero derives uncompressed depth/stencil as independent 64 KiB block surfaces and HTile // Prospero derives uncompressed depth/stencil as independent 64 KiB block surfaces.
// as 32 KiB metadata blocks covering 1024x512 pixels for a single-mip, single-slice target.
if (width > 0 && width <= 16384 && height > 0 && height <= 16384 && if (width > 0 && width <= 16384 && height > 0 && height <= 16384 &&
(z_format == 1 || z_format == 3) && stencil_format <= 1) { (z_format == 1 || z_format == 3) && stencil_format <= 1 && num_fragments_log2 <= 3) {
const uint32_t depth_bytes = z_format == 1 ? 2u : 4u; const uint32_t depth_bytes = z_format == 1 ? 2u : 4u;
const uint32_t depth_block_width = z_format == 1 ? 256u : 128u; uint32_t depth_block_width = 0;
uint32_t depth_block_height = 0;
uint32_t stencil_block_width = 0;
uint32_t stencil_block_height = 0;
const bool valid_blocks =
Gen5Msaa64KBBlockSizeFromElementBytes(depth_bytes, num_fragments_log2,
&depth_block_width, &depth_block_height) &&
(stencil_format == 0 ||
Gen5Msaa64KBBlockSizeFromElementBytes(1, num_fragments_log2, &stencil_block_width,
&stencil_block_height));
const uint32_t fragments = 1u << num_fragments_log2;
const uint64_t depth_bytes_total = const uint64_t depth_bytes_total =
static_cast<uint64_t>(AlignUp(width, depth_block_width)) * AlignUp(height, 128u) * valid_blocks ? static_cast<uint64_t>(AlignUp(width, depth_block_width)) *
depth_bytes; AlignUp(height, depth_block_height) * depth_bytes * fragments
: 0;
const uint64_t stencil_bytes_total = const uint64_t stencil_bytes_total =
stencil_format == 1 stencil_format == 1 && valid_blocks
? static_cast<uint64_t>(AlignUp(width, 256u)) * AlignUp(height, 256u) ? static_cast<uint64_t>(AlignUp(width, stencil_block_width)) *
AlignUp(height, stencil_block_height) * fragments
: 0; : 0;
const uint64_t htile_bytes_total = TileSizeAlign calculated_htile {};
htile ? static_cast<uint64_t>(AlignUp(width, 1024u) / 1024u) * const bool htile_valid = !htile || TileGetHtileSize(width, height, &calculated_htile);
(AlignUp(height, 512u) / 512u) * 32768u if (depth_bytes_total <= UINT32_MAX && stencil_bytes_total <= UINT32_MAX && htile_valid) {
: 0;
if (depth_bytes_total <= UINT32_MAX && stencil_bytes_total <= UINT32_MAX &&
htile_bytes_total <= UINT32_MAX) {
*depth_size = {static_cast<uint32_t>(depth_bytes_total), 65536}; *depth_size = {static_cast<uint32_t>(depth_bytes_total), 65536};
*stencil_size = stencil_format == 1 *stencil_size = stencil_format == 1
? TileSizeAlign {static_cast<uint32_t>(stencil_bytes_total), 65536} ? TileSizeAlign {static_cast<uint32_t>(stencil_bytes_total), 65536}
: TileSizeAlign {}; : TileSizeAlign {};
*htile_size = htile ? TileSizeAlign {static_cast<uint32_t>(htile_bytes_total), 32768} *htile_size = calculated_htile;
: TileSizeAlign {};
return true; return true;
} }
} }
@@ -2300,11 +2349,12 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t
return false; return false;
} }
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element) { uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
uint32_t num_fragments_log2) {
uint32_t block_width = 0; uint32_t block_width = 0;
uint32_t block_height = 0; uint32_t block_height = 0;
if (width == 0 || if (width == 0 || !Gen5Msaa64KBBlockSizeFromElementBytes(bytes_per_element, num_fragments_log2,
!Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width, &block_height)) { &block_width, &block_height)) {
return 0; return 0;
} }
const uint64_t pitch = (static_cast<uint64_t>(width) + block_width - 1u) & const uint64_t pitch = (static_cast<uint64_t>(width) + block_width - 1u) &
@@ -2312,19 +2362,27 @@ uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element) {
return pitch <= UINT32_MAX ? static_cast<uint32_t>(pitch) : 0; return pitch <= UINT32_MAX ? static_cast<uint32_t>(pitch) : 0;
} }
uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
uint32_t num_fragments_log2) {
return TileGetRenderTargetPitch(width, bytes_per_element, num_fragments_log2);
}
bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch, bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, TileSizeAlign* total_size) { uint32_t bytes_per_element, TileSizeAlign* total_size,
uint32_t num_fragments_log2) {
*total_size = {}; *total_size = {};
uint32_t block_width = 0; uint32_t block_width = 0;
uint32_t block_height = 0; uint32_t block_height = 0;
if (height == 0 || pitch == 0 || if (height == 0 || pitch == 0 ||
!Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width, &block_height) || !Gen5Msaa64KBBlockSizeFromElementBytes(bytes_per_element, num_fragments_log2, &block_width,
pitch != TileGetRenderTargetPitch(width, bytes_per_element)) { &block_height) ||
pitch != TileGetRenderTargetPitch(width, bytes_per_element, num_fragments_log2)) {
return false; return false;
} }
const uint64_t padded_height = (static_cast<uint64_t>(height) + block_height - 1u) & const uint64_t padded_height = (static_cast<uint64_t>(height) + block_height - 1u) &
~static_cast<uint64_t>(block_height - 1u); ~static_cast<uint64_t>(block_height - 1u);
const uint64_t size = static_cast<uint64_t>(pitch) * padded_height * bytes_per_element; const uint64_t size = static_cast<uint64_t>(pitch) * padded_height * bytes_per_element *
(1u << num_fragments_log2);
if (size == 0 || size > UINT32_MAX) { if (size == 0 || size > UINT32_MAX) {
return false; return false;
} }
+9 -3
View File
@@ -80,12 +80,18 @@ bool TileGetStandard4KBVolumeLayout(uint32_t format, uint32_t* bytes_per_element
uint32_t* texels_per_element_tall, uint32_t* block_width_log2, uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2, uint32_t* block_depth_log2); uint32_t* block_height_log2, uint32_t* block_depth_log2);
bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign* htile_size);
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format, bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
uint32_t stencil_format, bool htile, TileSizeAlign* stencil_size, uint32_t stencil_format, bool htile, TileSizeAlign* stencil_size,
TileSizeAlign* htile_size, TileSizeAlign* depth_size); TileSizeAlign* htile_size, TileSizeAlign* depth_size,
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element); uint32_t num_fragments_log2 = 0);
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
uint32_t num_fragments_log2 = 0);
uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
uint32_t num_fragments_log2 = 0);
bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch, bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, TileSizeAlign* total_size); uint32_t bytes_per_element, TileSizeAlign* total_size,
uint32_t num_fragments_log2 = 0);
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch, bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint32_t levels, uint32_t bytes_per_element, uint32_t levels,
TileSizeAlign* total_size, TileSizeOffset* level_sizes, TileSizeAlign* total_size, TileSizeOffset* level_sizes,
+5 -1
View File
@@ -111,6 +111,7 @@ struct VulkanImage {
uint32_t guest_pitch = 0; uint32_t guest_pitch = 0;
uint32_t layers = 1; uint32_t layers = 1;
uint32_t mip_levels = 1; uint32_t mip_levels = 1;
uint32_t samples = 1;
vk::Image image = nullptr; vk::Image image = nullptr;
vk::ImageView image_view[VIEW_MAX] = {}; vk::ImageView image_view[VIEW_MAX] = {};
vk::ImageLayout layout = vk::ImageLayout::eUndefined; vk::ImageLayout layout = vk::ImageLayout::eUndefined;
@@ -124,7 +125,9 @@ struct VideoOutVulkanImage: public VulkanImage {
struct DepthStencilVulkanImage: public VulkanImage { struct DepthStencilVulkanImage: public VulkanImage {
DepthStencilVulkanImage(): VulkanImage(VulkanImageType::DepthStencil) {} DepthStencilVulkanImage(): VulkanImage(VulkanImageType::DepthStencil) {}
bool compressed = false; bool compressed = false;
bool initial_depth_clear_pending = false;
bool initial_stencil_clear_pending = false;
}; };
struct GpuTextureVulkanImage: public VulkanImage { struct GpuTextureVulkanImage: public VulkanImage {
@@ -141,6 +144,7 @@ struct StorageTextureVulkanImage: public GpuTextureVulkanImage {
struct RenderTextureVulkanImage: public VulkanImage { struct RenderTextureVulkanImage: public VulkanImage {
RenderTextureVulkanImage(): VulkanImage(VulkanImageType::RenderTexture) {} RenderTextureVulkanImage(): VulkanImage(VulkanImageType::RenderTexture) {}
bool initial_clear_pending = false;
}; };
struct VulkanBuffer { struct VulkanBuffer {
@@ -66,14 +66,15 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->extent = {}; r->extent = {};
r->base_mip_level = 0; r->base_mip_level = 0;
r->buffer_size = 0; r->buffer_size = 0;
r->samples = 1;
r->color_clear_enable = false; r->color_clear_enable = false;
r->color_clear_value = {}; r->color_clear_value = {};
return; return;
} }
const bool msaa_compat = const auto samples = render_sample_count(rt.attrib.num_fragments);
color_msaa_single_sample_compatible(rt.attrib.num_samples, rt.attrib.num_fragments); if (samples == 0 || rt.attrib.num_samples != rt.attrib.num_fragments) {
if (!msaa_compat && (rt.attrib.num_samples != 0 || rt.attrib.num_fragments != 0)) { EXIT("unsupported render-target sample configuration: samples=%u fragments=%u\n",
EXIT("multisampled render targets are unsupported\n"); rt.attrib.num_samples, rt.attrib.num_fragments);
} }
const auto view = ResolveTargetViewInfo( const auto view = ResolveTargetViewInfo(
rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset); rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset);
@@ -93,16 +94,6 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
EXIT("unsupported render-target mip range: current=%u levels=%u\n", EXIT("unsupported render-target mip range: current=%u levels=%u\n",
rt.view.current_mip_level, levels); rt.view.current_mip_level, levels);
} }
if (msaa_compat) {
static std::atomic<uint32_t> logged_fragments = 0;
const uint32_t bit = 1u << rt.attrib.num_fragments;
if ((logged_fragments.fetch_or(bit, std::memory_order_relaxed) & bit) == 0) {
LOGF("RenderColorTarget: compatibility: rendering PS5 %ux samples/fragments as "
"single-sample\n",
bit);
}
}
if (graphics_debug_dump_enabled()) { if (graphics_debug_dump_enabled()) {
static std::atomic_uint log_count = 0; static std::atomic_uint log_count = 0;
const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed); const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed);
@@ -144,6 +135,9 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
if (!tile && levels > 1) { if (!tile && levels > 1) {
EXIT("linear mipmapped render targets are unsupported\n"); EXIT("linear mipmapped render targets are unsupported\n");
} }
if (samples > 1 && (!tile || levels != 1)) {
EXIT("multisampled render targets require a single-mip tiled surface\n");
}
width = rt.attrib2.width + 1; width = rt.attrib2.width + 1;
height = rt.attrib2.height + 1; height = rt.attrib2.height + 1;
@@ -156,13 +150,13 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
if (standard64 && if (standard64 &&
(rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 || levels != 1 || (rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 || levels != 1 ||
rt.view.current_mip_level != 0 || view.base_layer != 0 || view.image_layers != 1 || rt.view.current_mip_level != 0 || view.base_layer != 0 || view.image_layers != 1 ||
rt.attrib.num_samples != 0 || rt.attrib.num_fragments != 0 || bytes_per_element != 4 || samples != 1 || bytes_per_element != 4 || rt.pitch.pitch_div8_minus1 != 0 ||
rt.pitch.pitch_div8_minus1 != 0 || (rt.base.addr & 0xffffu) != 0 || (rt.base.addr & 0xffffu) != 0 || rt.info.fmask_compression_enable ||
rt.info.fmask_compression_enable || rt.info.fmask_data_compression_disable || rt.info.fmask_data_compression_disable || rt.info.fmask_one_frag_mode ||
rt.info.fmask_one_frag_mode || rt.info.cmask_fast_clear_enable || rt.info.cmask_fast_clear_enable || rt.info.dcc_compression_enable ||
rt.info.dcc_compression_enable || rt.info.cmask_is_linear != 0 || rt.info.cmask_is_linear != 0 || rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode ||
rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode || rt.cmask.addr != 0 || rt.cmask.addr != 0 || rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 ||
rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) { rt.dcc.data_write_on_dcc_clear_to_reg)) {
EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64 EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64
" dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u" " dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u"
" cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n", " cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n",
@@ -176,7 +170,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
pitch = standard64 pitch = standard64
? TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), ? TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float),
width, levels, rt.attrib3.tile_mode) width, levels, rt.attrib3.tile_mode)
: TileGetRenderTargetPitch(width, bytes_per_element); : TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
if (pitch == 0) { if (pitch == 0) {
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element); EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
} }
@@ -194,10 +188,10 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
valid_layout = layout.size != 0 && layout.align == 65536; valid_layout = layout.size != 0 && layout.align == 65536;
} else { } else {
valid_layout = valid_layout =
levels == 1 levels == 1 ? TileGetRenderTargetSize(width, height, pitch, bytes_per_element,
? TileGetRenderTargetSize(width, height, pitch, bytes_per_element, &layout) &layout, rt.attrib.num_fragments)
: TileGetRenderTargetMipLayout(width, height, pitch, bytes_per_element, levels, : TileGetRenderTargetMipLayout(width, height, pitch, bytes_per_element,
&layout, nullptr, nullptr); levels, &layout, nullptr, nullptr);
} }
if (!valid_layout) { if (!valid_layout) {
EXIT("unsupported render-target layout: %ux%u pitch=%u bytes=%u levels=%u\n", width, EXIT("unsupported render-target layout: %ux%u pitch=%u bytes=%u levels=%u\n", width,
@@ -211,7 +205,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
(static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u, size); (static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u, size);
} }
} else { } else {
size = static_cast<uint64_t>(pitch) * height * bytes_per_element; size = static_cast<uint64_t>(pitch) * height * bytes_per_element * samples;
} }
if (size == 0 || size > UINT64_MAX / view.image_layers) { if (size == 0 || size > UINT64_MAX / view.image_layers) {
EXIT("render-target memory footprint is invalid\n"); EXIT("render-target memory footprint is invalid\n");
@@ -241,10 +235,10 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
LOGF("RenderColorTarget: slot=%" PRIu32 " addr=0x%010" PRIx64 " size=0x%016" PRIx64 LOGF("RenderColorTarget: slot=%" PRIu32 " addr=0x%010" PRIx64 " size=0x%016" PRIx64
" extent=%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u" " extent=%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u"
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 " fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32
" tile=%s target=%s video_size=0x%016" PRIx64 " video_pitch=%" PRIu64 "\n", " samples=%u tile=%s target=%s video_size=0x%016" PRIx64 " video_pitch=%" PRIu64 "\n",
rt_slot, rt.base.addr, backing_size, width, height, rt.view.current_mip_level, rt_slot, rt.base.addr, backing_size, width, height, rt.view.current_mip_level,
view_extent.width, view_extent.height, levels, pitch, rt.info.format, view_extent.width, view_extent.height, levels, pitch, rt.info.format,
rt.info.channel_type, rt.info.channel_order, tile ? "tiled" : "linear", rt.info.channel_type, rt.info.channel_order, samples, tile ? "tiled" : "linear",
render_to_texture ? "RenderTexture" : "DisplayBuffer", video_image.size, render_to_texture ? "RenderTexture" : "DisplayBuffer", video_image.size,
video_image.pitch); video_image.pitch);
} }
@@ -262,6 +256,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
target.tile_mode = rt.attrib3.tile_mode; target.tile_mode = rt.attrib3.tile_mode;
target.levels = levels; target.levels = levels;
target.layers = view.image_layers; target.layers = view.image_layers;
target.samples = samples;
auto* texture_cache = g_render_ctx->GetTextureCache(); auto* texture_cache = g_render_ctx->GetTextureCache();
auto* buffer_vulkan = auto* buffer_vulkan =
texture_cache->FindRenderTarget(buffer, g_render_ctx->GetGraphicCtx(), target); texture_cache->FindRenderTarget(buffer, g_render_ctx->GetGraphicCtx(), target);
@@ -271,12 +266,18 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->vulkan_view = texture_cache->GetRenderTargetAttachmentView( r->vulkan_view = texture_cache->GetRenderTargetAttachmentView(
g_render_ctx->GetGraphicCtx(), buffer_vulkan, target.format, rt.view.current_mip_level, g_render_ctx->GetGraphicCtx(), buffer_vulkan, target.format, rt.view.current_mip_level,
view.base_layer, view.layer_count); view.base_layer, view.layer_count);
r->format = target.format; r->format = target.format;
r->extent = view_extent; r->extent = view_extent;
r->base_mip_level = rt.view.current_mip_level; r->base_mip_level = rt.view.current_mip_level;
r->buffer_size = backing_size; r->buffer_size = backing_size;
r->export_mapping = target_format.export_mapping; r->samples = samples;
r->export_mapping = target_format.export_mapping;
r->color_clear_enable = buffer_vulkan->initial_clear_pending;
r->color_clear_value = {};
} else { } else {
if (samples != 1) {
EXIT("multisampled display render targets are unsupported\n");
}
const auto layout = static_cast<Prospero::ChannelLayout>(rt.info.format); const auto layout = static_cast<Prospero::ChannelLayout>(rt.info.format);
const auto type = static_cast<Prospero::ChannelType>(rt.info.channel_type); const auto type = static_cast<Prospero::ChannelType>(rt.info.channel_type);
const auto order = static_cast<Prospero::ChannelOrder>(rt.info.channel_order); const auto order = static_cast<Prospero::ChannelOrder>(rt.info.channel_order);
@@ -312,6 +313,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->extent = video_image.image->extent; r->extent = video_image.image->extent;
r->base_mip_level = 0; r->base_mip_level = 0;
r->buffer_size = video_image.size; r->buffer_size = video_image.size;
r->samples = 1;
r->export_mapping = target_format.export_mapping; r->export_mapping = target_format.export_mapping;
} }
} }
@@ -33,6 +33,7 @@ struct RenderColorInfo {
uint64_t base_addr = 0; uint64_t base_addr = 0;
uint64_t buffer_size = 0; uint64_t buffer_size = 0;
uint32_t target_slot = 0; uint32_t target_slot = 0;
uint32_t samples = 1;
Prospero::ColorComponentMapping export_mapping; Prospero::ColorComponentMapping export_mapping;
bool color_clear_enable = false; bool color_clear_enable = false;
vk::ClearColorValue color_clear_value {}; vk::ClearColorValue color_clear_value {};
@@ -580,6 +580,14 @@ void CommandBuffer::BeginRenderPass(VulkanFramebuffer* framebuffer, RenderColorI
for (uint32_t i = 0; i < color_count; i++) { for (uint32_t i = 0; i < color_count; i++) {
colors[i].vulkan_buffer->layout = RENDER_COLOR_IMAGE_LAYOUT; colors[i].vulkan_buffer->layout = RENDER_COLOR_IMAGE_LAYOUT;
if (colors[i].vulkan_buffer->type == VulkanImageType::RenderTexture) {
static_cast<RenderTextureVulkanImage*>(colors[i].vulkan_buffer)->initial_clear_pending =
false;
}
}
if (with_depth) {
depth->vulkan_buffer->initial_depth_clear_pending = false;
depth->vulkan_buffer->initial_stencil_clear_pending = false;
} }
} }
+6 -7
View File
@@ -334,7 +334,7 @@ static void RtCheck(const HW::RenderTarget& rt) {
if (rt.attrib.num_samples != 0x00000000 || rt.attrib.num_fragments != 0x00000000) { if (rt.attrib.num_samples != 0x00000000 || rt.attrib.num_fragments != 0x00000000) {
static bool logged = false; static bool logged = false;
if (!logged) { if (!logged) {
LOGF("RenderTarget: temporary: rendering PS5 MSAA color target as single-sample, " LOGF("RenderTarget: using native PS5 MSAA color target, "
"samples=0x%08" PRIx32 " fragments=0x%08" PRIx32 "\n", "samples=0x%08" PRIx32 " fragments=0x%08" PRIx32 "\n",
rt.attrib.num_samples, rt.attrib.num_fragments); rt.attrib.num_samples, rt.attrib.num_fragments);
logged = true; logged = true;
@@ -534,7 +534,7 @@ static void ZCheck(const HW::DepthRenderTarget& z) {
if (z.z_info.num_samples != 0x00000000) { if (z.z_info.num_samples != 0x00000000) {
static bool logged = false; static bool logged = false;
if (!logged) { if (!logged) {
LOGF("DepthTarget: temporary: ignoring num_samples=0x%08" PRIx32 "\n", LOGF("DepthTarget: using native num_samples=0x%08" PRIx32 "\n",
z.z_info.num_samples); z.z_info.num_samples);
logged = true; logged = true;
} }
@@ -932,7 +932,7 @@ static void EqaaCheck(const HW::EqaaControl& c) {
c.incoherent_eqaa_reads || c.interpolate_comp_z || c.static_anchor_associations) { c.incoherent_eqaa_reads || c.interpolate_comp_z || c.static_anchor_associations) {
static std::atomic<uint32_t> log_count {0}; static std::atomic<uint32_t> log_count {0};
if (log_count.fetch_add(1) < 16) { if (log_count.fetch_add(1) < 16) {
LOGF("\t warning: unsupported PS5 EQAA state, rendering with single-sample fallback\n"); LOGF("\t warning: unsupported PS5 EQAA controls use native Vulkan MSAA defaults\n");
} }
} }
} }
@@ -962,8 +962,8 @@ static void AaCheck(const HW::AaSampleControl& c, const HW::AaConfig& cf) {
cf.max_sample_dist != 0 || cf.msaa_exposed_samples != 0) { cf.max_sample_dist != 0 || cf.msaa_exposed_samples != 0) {
static std::atomic<uint32_t> log_count {0}; static std::atomic<uint32_t> log_count {0};
if (log_count.fetch_add(1) < 16) { if (log_count.fetch_add(1) < 16) {
LOGF("\t warning: unsupported PS5 AA/MSAA state, rendering with single-sample " LOGF("\t warning: unsupported PS5 sample locations use native Vulkan locations: "
"fallback: samples=%" PRIu8 ", exposed=%" PRIu8 ", max_dist=%" PRIu8 "\n", "samples=%" PRIu8 ", exposed=%" PRIu8 ", max_dist=%" PRIu8 "\n",
cf.msaa_num_samples, cf.msaa_exposed_samples, cf.max_sample_dist); cf.msaa_num_samples, cf.msaa_exposed_samples, cf.max_sample_dist);
} }
} }
@@ -1058,8 +1058,7 @@ static void VpCheck(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc
static std::atomic<uint32_t> log_count {0}; static std::atomic<uint32_t> log_count {0};
if (log_count.fetch_add(1) < 16) { if (log_count.fetch_add(1) < 16) {
LOGF("\t warning: unsupported PS5 MSAA raster state, rendering with single-sample " LOGF("\t warning: unsupported PS5 MSAA raster controls use native Vulkan defaults\n");
"fallback\n");
} }
} }
// EXIT_NOT_IMPLEMENTED(smc.vport_scissor_enable); // EXIT_NOT_IMPLEMENTED(smc.vport_scissor_enable);
@@ -63,29 +63,30 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(GraphicContext* ctx, [[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(GraphicContext* ctx,
const DepthFormatPolicy& policy, const DepthFormatPolicy& policy,
bool has_stencil) { bool has_stencil,
uint32_t samples) {
if (ctx == nullptr) {
return vk::Format::eUndefined;
}
const auto required_samples = vulkan_sample_count(samples);
const auto supports = [&](vk::Format format) {
vk::ImageFormatProperties properties {};
return format != vk::Format::eUndefined &&
ctx->GetImageFormatProperties(format, vk::ImageType::e2D, vk::ImageTiling::eOptimal,
DepthTargetImageUsage(), vk::ImageCreateFlags {},
&properties) == vk::Result::eSuccess &&
static_cast<bool>(properties.sampleCounts & required_samples);
};
if (!has_stencil) { if (!has_stencil) {
return policy.depth_attachment_format; return supports(policy.depth_attachment_format) ? policy.depth_attachment_format
: vk::Format::eUndefined;
} }
switch (policy.depth_format) { for (const auto format: policy.stencil_attachment_formats) {
case Prospero::DepthFormat::kZ32F: return policy.stencil_attachment_formats.front(); if (supports(format)) {
case Prospero::DepthFormat::kZ16: { return format;
if (ctx == nullptr) {
return vk::Format::eUndefined;
}
for (const auto format: policy.stencil_attachment_formats) {
vk::ImageFormatProperties properties {};
if (ctx->GetImageFormatProperties(format, vk::ImageType::e2D,
vk::ImageTiling::eOptimal,
DepthTargetImageUsage(), vk::ImageCreateFlags {},
&properties) == vk::Result::eSuccess) {
return format;
}
}
return vk::Format::eUndefined;
} }
default: return vk::Format::eUndefined;
} }
return vk::Format::eUndefined;
} }
// NOLINTNEXTLINE(readability-function-cognitive-complexity) // NOLINTNEXTLINE(readability-function-cognitive-complexity)
@@ -142,8 +143,11 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
} }
const bool has_stencil = const bool has_stencil =
z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid); z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid);
const bool has_htile = z.z_info.tile_surface_enable; const bool has_htile = z.z_info.tile_surface_enable;
const bool msaa_compat = depth_msaa_single_sample_compatible(z.z_info.num_samples); const auto samples = render_sample_count(z.z_info.num_samples);
if (samples == 0) {
DepthFatal("unsupported depth fragment count: %u", z.z_info.num_samples);
}
const bool htile_stencil_compat = depth_htile_stencil_acceleration_compatible( const bool htile_stencil_compat = depth_htile_stencil_acceleration_compatible(
has_stencil, has_htile, z.stencil_info.tile_stencil_disable); has_stencil, has_htile, z.stencil_info.tile_stencil_disable);
const auto view = ResolveTargetViewInfo(z.depth_view.slice_start, z.depth_view.slice_max); const auto view = ResolveTargetViewInfo(z.depth_view.slice_start, z.depth_view.slice_max);
@@ -162,28 +166,20 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
rc.copy_sample != 0 || z.z_info.expclear_enabled || z.stencil_info.expclear_enabled || rc.copy_sample != 0 || z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
z.z_info.embedded_sample_locations || z.z_info.partially_resident || z.z_info.embedded_sample_locations || z.z_info.partially_resident ||
z.stencil_info.partially_resident || z.z_info.plane_compression != 0 || z.stencil_info.partially_resident || z.z_info.plane_compression != 0 ||
(z.z_info.num_samples != 0 && !msaa_compat) || z.z_info.num_mip_levels != 0 || z.z_info.num_mip_levels != 0 || z.z_info.tile_mode_index != 0 ||
z.z_info.tile_mode_index != 0 || z.z_info.zrange_precision > 1 || z.z_info.zrange_precision > 1 || z.depth_view.current_mip_level != 0 ||
z.depth_view.current_mip_level != 0 || z.depth_info.addr5_swizzle_mask != 0 || z.depth_info.addr5_swizzle_mask != 0 || z.depth_info.array_mode != 0 ||
z.depth_info.array_mode != 0 || z.depth_info.pipe_config != 0 || z.depth_info.pipe_config != 0 || z.depth_info.bank_width != 0 ||
z.depth_info.bank_width != 0 || z.depth_info.bank_height != 0 || z.depth_info.bank_height != 0 || z.depth_info.macro_tile_aspect != 0 ||
z.depth_info.macro_tile_aspect != 0 || z.depth_info.num_banks != 0 || z.depth_info.num_banks != 0 || z.htile_surface.linear != 0 ||
z.htile_surface.linear != 0 || z.htile_surface.full_cache != 0 || z.htile_surface.full_cache != 0 || z.htile_surface.htile_uses_preload_win != 0 ||
z.htile_surface.htile_uses_preload_win != 0 || z.htile_surface.preload != 0 || z.htile_surface.preload != 0 || z.htile_surface.prefetch_width != 0 ||
z.htile_surface.prefetch_width != 0 || z.htile_surface.prefetch_height != 0 || z.htile_surface.prefetch_height != 0 || z.htile_surface.dst_outside_zero_to_one != 0 ||
z.htile_surface.dst_outside_zero_to_one != 0 || z.z_read_base_addr == 0 || z.z_read_base_addr == 0 || z.z_write_base_addr != z.z_read_base_addr ||
z.z_write_base_addr != z.z_read_base_addr || (z.z_read_base_addr & 0xffffu) != 0 || (z.z_read_base_addr & 0xffffu) != 0 ||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) { dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
DepthFatal("unsupported depth register state"); DepthFatal("unsupported depth register state");
} }
if (msaa_compat) {
static std::atomic<uint32_t> logged_fragments = 0;
const uint32_t bit = 1u << z.z_info.num_samples;
if ((logged_fragments.fetch_or(bit, std::memory_order_relaxed) & bit) == 0) {
LOGF("DepthTarget: compatibility: rendering PS5 %ux depth fragments as single-sample\n",
bit);
}
}
if (has_stencil) { if (has_stencil) {
// Prospero defines Hi-Stencil as HTile-backed acceleration of the logical stencil plane. // Prospero defines Hi-Stencil as HTile-backed acceleration of the logical stencil plane.
// Keep the plane native in Vulkan while tracking HTile separately. // Keep the plane native in Vulkan while tracking HTile separately.
@@ -236,55 +232,35 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
DepthFatal("unsupported depth/stencil format pair"); DepthFatal("unsupported depth/stencil format pair");
} }
const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil); const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil);
r->format = r->format = ResolveHostDepthAttachmentFormat(g_render_ctx->GetGraphicCtx(), *policy,
ResolveHostDepthAttachmentFormat(g_render_ctx->GetGraphicCtx(), *policy, has_stencil); has_stencil, samples);
if (r->format == vk::Format::eUndefined) { if (r->format == vk::Format::eUndefined) {
DepthFatal("no host depth/stencil format supports required usage for %s", DepthFatal("no host depth/stencil format supports required usage for %s",
VulkanToString(ideal_format).c_str()); VulkanToString(ideal_format).c_str());
} }
const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format); const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format);
const uint32_t bytes = policy->bytes_per_element; const uint32_t bytes = policy->bytes_per_element;
const auto pitch = TileGetTexturePitch(guest_format, width, 1, const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch || if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
(static_cast<uint64_t>(z.height_div8_minus1) + 1u) * 8u != (static_cast<uint64_t>(z.height_div8_minus1) + 1u) * 8u !=
((static_cast<uint64_t>(height) + 7u) & ~7ull))) { ((static_cast<uint64_t>(height) + 7u) & ~7ull))) {
DepthFatal("encoded depth pitch or height mismatch"); DepthFatal("encoded depth pitch or height mismatch");
} }
const uint32_t block_width = bytes == 2 ? 256u : 128u; TileSizeAlign depth_size {};
const uint64_t padded_width = TileSizeAlign stencil_size {};
(static_cast<uint64_t>(pitch) + block_width - 1u) & ~(block_width - 1u); TileSizeAlign htile_size {};
const uint64_t padded_height = if (!TileGetDepthSize(width, height, 0, z.z_info.format, z.stencil_info.format, has_htile,
(static_cast<uint64_t>(height) + 127u) & ~static_cast<uint64_t>(127u); &stencil_size, &htile_size, &depth_size, z.z_info.num_samples) ||
if (padded_width > UINT64_MAX / padded_height || depth_size.align != 65536 || depth_size.size == 0 ||
padded_width * padded_height > UINT64_MAX / bytes) { (has_stencil != (stencil_size.align == 65536 && stencil_size.size != 0)) ||
DepthFatal("depth footprint overflow"); (has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
DepthFatal("unsupported depth/stencil/HTile footprint");
} }
const uint64_t expected_size = padded_width * padded_height * bytes; if (z.pitch_height_valid &&
TileSizeAlign depth_size {}; (static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u != depth_size.size) {
TileSizeAlign stencil_size {};
TileSizeAlign htile_size {};
if (has_stencil || has_htile) {
if (!TileGetDepthSize(width, height, 0, z.z_info.format, z.stencil_info.format, has_htile,
&stencil_size, &htile_size, &depth_size) ||
depth_size.align != 65536 || depth_size.size != expected_size ||
(has_stencil != (stencil_size.align == 65536 && stencil_size.size != 0)) ||
(has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
DepthFatal("unsupported depth/stencil/HTile footprint");
}
} else {
TileGetTextureTotalSize(guest_format, width, height, 1, pitch, 1,
Prospero::GpuEnumValue(Prospero::TileMode::kDepth), false,
&depth_size);
}
if (expected_size == 0 || expected_size > UINT32_MAX || depth_size.align != 65536 ||
depth_size.size != expected_size ||
(z.pitch_height_valid &&
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u != expected_size)) {
DepthFatal("depth footprint mismatch: extent=%ux%u pitch=%u expected=0x%016" PRIx64 DepthFatal("depth footprint mismatch: extent=%ux%u pitch=%u expected=0x%016" PRIx64
" calculated=0x%016" PRIx64 "/0x%016" PRIx64 " align=0x%016" PRIx64 " encoded_valid=%u encoded=0x%016" PRIx64,
" encoded_valid=%u encoded=0x%016" PRIx64, width, height, pitch, depth_size.size, depth_size.align,
width, height, pitch, expected_size, depth_size.size, depth_size.align,
z.pitch_height_valid ? 1u : 0u, z.pitch_height_valid ? 1u : 0u,
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u); (static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u);
} }
@@ -304,6 +280,7 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->htile = has_htile; r->htile = has_htile;
r->width = width; r->width = width;
r->height = height; r->height = height;
r->samples = samples;
r->depth_buffer_size = depth_backing_size; r->depth_buffer_size = depth_backing_size;
r->depth_buffer_vaddr = z.z_read_base_addr; r->depth_buffer_vaddr = z.z_read_base_addr;
r->stencil_buffer_size = has_stencil ? stencil_backing_size : 0; r->stencil_buffer_size = has_stencil ? stencil_backing_size : 0;
@@ -312,7 +289,8 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->htile_buffer_vaddr = has_htile ? z.htile_data_base_addr : 0; r->htile_buffer_vaddr = has_htile ? z.htile_data_base_addr : 0;
auto* cache = g_render_ctx->GetTextureCache(); auto* cache = g_render_ctx->GetTextureCache();
if (has_htile) { if (has_htile) {
cache->RegisterMeta(r->htile_buffer_vaddr, r->htile_buffer_size, view.image_layers); cache->RegisterMeta(g_render_ctx->GetGraphicCtx(), r->htile_buffer_vaddr,
r->htile_buffer_size, view.image_layers);
} }
if (has_htile && rc.depth_clear_enable && !cache->ClearMeta(z.htile_data_base_addr)) { if (has_htile && rc.depth_clear_enable && !cache->ClearMeta(z.htile_data_base_addr)) {
DepthFatal("failed to acquire HTile metadata for a depth clear"); DepthFatal("failed to acquire HTile metadata for a depth clear");
@@ -386,7 +364,9 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
info.bytes_per_element = bytes; info.bytes_per_element = bytes;
info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth); info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
info.layers = view.image_layers; info.layers = view.image_layers;
info.samples = samples;
info.depth_load_clear = r->depth_load_clear_enable; info.depth_load_clear = r->depth_load_clear_enable;
info.depth_access = depth_active;
info.stencil_load_clear = rc.stencil_clear_enable; info.stencil_load_clear = rc.stencil_clear_enable;
info.stencil_access = info.stencil_access =
r->stencil_clear_enable || r->stencil_clear_enable ||
@@ -398,6 +378,14 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
r->vulkan_buffer = cache->FindDepthTarget(buffer, g_render_ctx->GetGraphicCtx(), info); r->vulkan_buffer = cache->FindDepthTarget(buffer, g_render_ctx->GetGraphicCtx(), info);
r->vulkan_view = cache->GetDepthTargetAttachmentView( r->vulkan_view = cache->GetDepthTargetAttachmentView(
g_render_ctx->GetGraphicCtx(), r->vulkan_buffer, view.base_layer, view.layer_count); g_render_ctx->GetGraphicCtx(), r->vulkan_buffer, view.base_layer, view.layer_count);
if (r->vulkan_buffer->initial_depth_clear_pending) {
r->depth_load_clear_enable = true;
r->depth_clear_value = 0.0f;
}
if (r->vulkan_buffer->initial_stencil_clear_pending) {
r->stencil_clear_enable = true;
r->stencil_clear_value = 0;
}
if (meta_clear && !cache->TouchMeta(z.htile_data_base_addr, z.depth_view.slice_start, false)) { if (meta_clear && !cache->TouchMeta(z.htile_data_base_addr, z.depth_view.slice_start, false)) {
DepthFatal("failed to consume HTile clear state"); DepthFatal("failed to consume HTile clear state");
} }
@@ -25,6 +25,7 @@ struct RenderDepthInfo {
vk::Format format = vk::Format::eUndefined; vk::Format format = vk::Format::eUndefined;
uint32_t width = 0; uint32_t width = 0;
uint32_t height = 0; uint32_t height = 0;
uint32_t samples = 1;
bool htile = false; bool htile = false;
uint64_t depth_buffer_size = 0; uint64_t depth_buffer_size = 0;
uint64_t depth_buffer_vaddr = 0; uint64_t depth_buffer_vaddr = 0;
@@ -285,6 +285,12 @@ TargetTextureViewInfo ResolveTargetTextureView(const ShaderRecompiler::IR::Image
base_layer == 0 && image_layers == 1 base_layer == 0 && image_layers == 1
? TargetTextureViewInfo {vk::ImageViewType::e2D, 0, 1} ? TargetTextureViewInfo {vk::ImageViewType::e2D, 0, 1}
: TargetTextureViewInfo {}; : TargetTextureViewInfo {};
case Prospero::ImageType::kCube:
if (resource.dimension != ShaderRecompiler::Decoder::ImageDimension::Dim2DArray ||
base_layer >= image_layers || (image_layers - base_layer) % 6u != 0) {
return {};
}
return {vk::ImageViewType::e2DArray, base_layer, image_layers - base_layer};
case Prospero::ImageType::kColor2DArray: case Prospero::ImageType::kColor2DArray:
if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
base_layer == 0 && image_layers == 1) { base_layer == 0 && image_layers == 1) {
@@ -30,6 +30,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
uint32_t color_count = 0; uint32_t color_count = 0;
VulkanImage* first_color = nullptr; VulkanImage* first_color = nullptr;
vk::Extent2D first_color_extent = {}; vk::Extent2D first_color_extent = {};
uint32_t attachment_samples = 0;
for (uint32_t i = 0; i < requested_color_count; i++) { for (uint32_t i = 0; i < requested_color_count; i++) {
with_color[i] = (colors[i].vulkan_buffer != nullptr); with_color[i] = (colors[i].vulkan_buffer != nullptr);
if (!with_color[i]) { if (!with_color[i]) {
@@ -38,6 +39,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
if (first_color == nullptr) { if (first_color == nullptr) {
first_color = colors[i].vulkan_buffer; first_color = colors[i].vulkan_buffer;
first_color_extent = colors[i].extent; first_color_extent = colors[i].extent;
attachment_samples = colors[i].samples;
} else if (colors[i].extent.width != first_color_extent.width || } else if (colors[i].extent.width != first_color_extent.width ||
colors[i].extent.height != first_color_extent.height) { colors[i].extent.height != first_color_extent.height) {
LOGF("Framebuffer: temporary: dropping mismatched MRT%u attachment color0=%ux%u " LOGF("Framebuffer: temporary: dropping mismatched MRT%u attachment color0=%ux%u "
@@ -47,8 +49,34 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
with_color[i] = false; with_color[i] = false;
break; break;
} }
if (colors[i].samples != attachment_samples ||
colors[i].vulkan_buffer->samples != colors[i].samples) {
EXIT("Framebuffer: mismatched color attachment samples at slot %u, expected=%u "
"requested=%u image=%u\n",
i, attachment_samples, colors[i].samples, colors[i].vulkan_buffer->samples);
}
color_count++; color_count++;
} }
if (with_depth) {
if (depth->samples != depth->vulkan_buffer->samples) {
EXIT("Framebuffer: depth attachment sample identity mismatch, requested=%u image=%u\n",
depth->samples, depth->vulkan_buffer->samples);
}
if (attachment_samples == 0) {
attachment_samples = depth->samples;
} else if (attachment_samples != depth->samples) {
EXIT(
"Framebuffer: mixed color/depth sample counts are unsupported, color=%u depth=%u\n",
attachment_samples, depth->samples);
}
}
if (!with_depth && color_count == 0) {
LOGF("Framebuffer: warning: no color or depth attachment\n");
return nullptr;
}
if (vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {}) {
EXIT("Framebuffer: invalid attachment sample count %u\n", attachment_samples);
}
vk::ImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {}; vk::ImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {};
for (auto& layout: color_layout) { for (auto& layout: color_layout) {
layout = RENDER_COLOR_IMAGE_LAYOUT; layout = RENDER_COLOR_IMAGE_LAYOUT;
@@ -107,11 +135,6 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
} }
} }
if (!with_depth && color_count == 0) {
LOGF("Framebuffer: warning: no color or depth attachment\n");
return nullptr;
}
EXIT_NOT_IMPLEMENTED(with_depth && first_color != nullptr && EXIT_NOT_IMPLEMENTED(with_depth && first_color != nullptr &&
(first_color_extent.width != depth->vulkan_buffer->extent.width || (first_color_extent.width != depth->vulkan_buffer->extent.width ||
first_color_extent.height != depth->vulkan_buffer->extent.height)); first_color_extent.height != depth->vulkan_buffer->extent.height));
@@ -123,6 +146,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
auto* framebuffer = new VulkanFramebuffer; auto* framebuffer = new VulkanFramebuffer;
framebuffer->render_pass = nullptr; framebuffer->render_pass = nullptr;
framebuffer->framebuffer = nullptr; framebuffer->framebuffer = nullptr;
framebuffer->samples = attachment_samples;
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) { for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
framebuffer->color_layout[i] = color_layout[i]; framebuffer->color_layout[i] = color_layout[i];
} }
@@ -136,7 +160,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
for (uint32_t i = 0; i < color_count; i++) { for (uint32_t i = 0; i < color_count; i++) {
attachments[i].flags = {}; attachments[i].flags = {};
attachments[i].format = colors[i].format; attachments[i].format = colors[i].format;
attachments[i].samples = vk::SampleCountFlagBits::e1; attachments[i].samples = vulkan_sample_count(attachment_samples);
attachments[i].loadOp = (colors[i].color_clear_enable ? vk::AttachmentLoadOp::eClear attachments[i].loadOp = (colors[i].color_clear_enable ? vk::AttachmentLoadOp::eClear
: vk::AttachmentLoadOp::eLoad); : vk::AttachmentLoadOp::eLoad);
attachments[i].storeOp = vk::AttachmentStoreOp::eStore; attachments[i].storeOp = vk::AttachmentStoreOp::eStore;
@@ -149,7 +173,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
const uint32_t depth_attachment = color_count; const uint32_t depth_attachment = color_count;
attachments[depth_attachment].flags = {}; attachments[depth_attachment].flags = {};
attachments[depth_attachment].format = depth->format; attachments[depth_attachment].format = depth->format;
attachments[depth_attachment].samples = vk::SampleCountFlagBits::e1; attachments[depth_attachment].samples = vulkan_sample_count(attachment_samples);
attachments[depth_attachment].loadOp = attachments[depth_attachment].loadOp =
(depth->depth_load_clear_enable ? vk::AttachmentLoadOp::eClear (depth->depth_load_clear_enable ? vk::AttachmentLoadOp::eClear
: vk::AttachmentLoadOp::eLoad); : vk::AttachmentLoadOp::eLoad);
@@ -229,8 +253,8 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
for (uint32_t i = 0; i < color_count; i++) { for (uint32_t i = 0; i < color_count; i++) {
color_formats[i] = colors[i].format; color_formats[i] = colors[i].format;
} }
framebuffer->render_pass_id = framebuffer->render_pass_id = render_pass_compat_id(
render_pass_compat_id(color_count, color_formats, with_depth, depth->format, depth_layout); color_count, color_formats, with_depth, depth->format, depth_layout, attachment_samples);
EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr); EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr);
@@ -22,13 +22,14 @@ struct VulkanFramebuffer {
vk::RenderPass render_pass = nullptr; vk::RenderPass render_pass = nullptr;
uint64_t render_pass_id = 0; uint64_t render_pass_id = 0;
vk::Framebuffer framebuffer = nullptr; vk::Framebuffer framebuffer = nullptr;
uint32_t samples = 1;
vk::ImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {}; vk::ImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {};
vk::ImageLayout depth_layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; vk::ImageLayout depth_layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
}; };
inline uint64_t render_pass_compat_id(uint32_t color_count, const vk::Format* color_formats, inline uint64_t render_pass_compat_id(uint32_t color_count, const vk::Format* color_formats,
bool with_depth, vk::Format depth_format, bool with_depth, vk::Format depth_format,
vk::ImageLayout depth_layout) { vk::ImageLayout depth_layout, uint32_t samples) {
uint64_t id = 0xcbf29ce484222325ull; uint64_t id = 0xcbf29ce484222325ull;
auto mix = [&id](uint64_t v) { auto mix = [&id](uint64_t v) {
id ^= v; id ^= v;
@@ -42,7 +43,7 @@ inline uint64_t render_pass_compat_id(uint32_t color_count, const vk::Format* co
mix(with_depth ? 1u : 0u); mix(with_depth ? 1u : 0u);
mix(static_cast<uint32_t>(depth_format)); mix(static_cast<uint32_t>(depth_format));
mix(static_cast<uint32_t>(depth_layout)); mix(static_cast<uint32_t>(depth_layout));
mix(static_cast<uint32_t>(vk::SampleCountFlagBits::e1)); mix(samples);
return id; return id;
} }
+30 -16
View File
@@ -10,6 +10,7 @@
#include "graphics/host_gpu/renderer/framebufferCache.h" #include "graphics/host_gpu/renderer/framebufferCache.h"
#include "graphics/host_gpu/renderer/imageView.h" #include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/renderContext.h" #include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/transfer.h" #include "graphics/host_gpu/transfer.h"
#include "graphics/host_gpu/vma.h" #include "graphics/host_gpu/vma.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
@@ -71,20 +72,24 @@ vk::ImageCreateFlags RenderTargetCreateFlags(vk::Format format) {
} }
vk::ImageUsageFlags RenderTargetUsage(GraphicContext* ctx, vk::Format format, vk::ImageUsageFlags RenderTargetUsage(GraphicContext* ctx, vk::Format format,
vk::ImageCreateFlags flags) { vk::ImageCreateFlags flags, uint32_t samples) {
auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) | auto usage = static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eColorAttachment) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) | static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferSrc) |
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst) | static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eTransferDst);
static_cast<vk::ImageUsageFlags>(vk::ImageUsageFlagBits::eSampled); if (samples == 1) {
if (RenderTargetSupportsStorage(ctx, format, flags)) { usage |= vk::ImageUsageFlagBits::eSampled;
usage |= vk::ImageUsageFlagBits::eStorage; if (RenderTargetSupportsStorage(ctx, format, flags)) {
usage |= vk::ImageUsageFlagBits::eStorage;
}
} }
vk::ImageFormatProperties properties {}; vk::ImageFormatProperties properties {};
if (ctx->GetImageFormatProperties(format, vk::ImageType::e2D, vk::ImageTiling::eOptimal, usage, if (ctx->GetImageFormatProperties(format, vk::ImageType::e2D, vk::ImageTiling::eOptimal, usage,
flags, &properties) != vk::Result::eSuccess) { flags, &properties) != vk::Result::eSuccess ||
!static_cast<bool>(properties.sampleCounts & vulkan_sample_count(samples))) {
EXIT("TextureCache: render-target format does not support required usage, format=%d " EXIT("TextureCache: render-target format does not support required usage, format=%d "
"usage=0x%x\n", "usage=0x%x samples=%u supported=0x%x\n",
static_cast<int>(format), static_cast<vk::ImageUsageFlags::MaskType>(usage)); static_cast<int>(format), static_cast<vk::ImageUsageFlags::MaskType>(usage), samples,
static_cast<vk::SampleCountFlags::MaskType>(properties.sampleCounts));
} }
return usage; return usage;
} }
@@ -172,6 +177,10 @@ void UploadRenderTargetLayers(GraphicContext* ctx, RenderTextureVulkanImage* ima
"info_layers=%u image_layers=%u size=0x%016" PRIx64 "\n", "info_layers=%u image_layers=%u size=0x%016" PRIx64 "\n",
base_layer, layer_count, info.layers, image != nullptr ? image->layers : 0, info.size); base_layer, layer_count, info.layers, image != nullptr ? image->layers : 0, info.size);
} }
if (info.samples != 1 || image->samples != 1) {
EXIT("TextureCache: multisampled render-target upload is unsupported, samples=%u/%u\n",
info.samples, image->samples);
}
if (refresh) { if (refresh) {
Transfer::WaitForGraphicsIdle(ctx); Transfer::WaitForGraphicsIdle(ctx);
} }
@@ -230,6 +239,7 @@ RenderTextureVulkanImage* CreateRenderTarget(GraphicContext* ctx, const RenderTa
image->format = info.format; image->format = info.format;
image->mip_levels = info.levels; image->mip_levels = info.levels;
image->layers = info.layers; image->layers = info.layers;
image->samples = info.samples;
image->layout = vk::ImageLayout::eUndefined; image->layout = vk::ImageLayout::eUndefined;
vk::ImageCreateInfo create {}; vk::ImageCreateInfo create {};
create.sType = vk::StructureType::eImageCreateInfo; create.sType = vk::StructureType::eImageCreateInfo;
@@ -241,9 +251,9 @@ RenderTextureVulkanImage* CreateRenderTarget(GraphicContext* ctx, const RenderTa
create.format = info.format; create.format = info.format;
create.tiling = vk::ImageTiling::eOptimal; create.tiling = vk::ImageTiling::eOptimal;
create.initialLayout = vk::ImageLayout::eUndefined; create.initialLayout = vk::ImageLayout::eUndefined;
create.usage = RenderTargetUsage(ctx, info.format, create.flags); create.usage = RenderTargetUsage(ctx, info.format, create.flags, info.samples);
create.sharingMode = vk::SharingMode::eExclusive; create.sharingMode = vk::SharingMode::eExclusive;
create.samples = vk::SampleCountFlagBits::e1; create.samples = vulkan_sample_count(info.samples);
image->memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal; image->memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
if (!VulkanCreateImage(ctx, create, image)) { if (!VulkanCreateImage(ctx, create, image)) {
EXIT("TextureCache: failed to create render target, addr=0x%016" PRIx64 EXIT("TextureCache: failed to create render target, addr=0x%016" PRIx64
@@ -266,20 +276,24 @@ DepthStencilVulkanImage* CreateDepthTarget(GraphicContext* ctx, const DepthTarge
create.initialLayout = vk::ImageLayout::eUndefined; create.initialLayout = vk::ImageLayout::eUndefined;
create.usage = DepthTargetImageUsage(); create.usage = DepthTargetImageUsage();
create.sharingMode = vk::SharingMode::eExclusive; create.sharingMode = vk::SharingMode::eExclusive;
create.samples = vk::SampleCountFlagBits::e1; create.samples = vulkan_sample_count(info.samples);
vk::ImageFormatProperties properties {}; vk::ImageFormatProperties properties {};
if (ctx->GetImageFormatProperties(info.format, vk::ImageType::e2D, vk::ImageTiling::eOptimal, if (ctx->GetImageFormatProperties(info.format, vk::ImageType::e2D, vk::ImageTiling::eOptimal,
create.usage, vk::ImageCreateFlags {}, create.usage, vk::ImageCreateFlags {},
&properties) != vk::Result::eSuccess) { &properties) != vk::Result::eSuccess ||
EXIT("TextureCache: depth format does not support required usage, format=%d usage=0x%x\n", !static_cast<bool>(properties.sampleCounts & create.samples)) {
EXIT("TextureCache: depth format does not support required usage, format=%d usage=0x%x "
"samples=%u supported=0x%x\n",
static_cast<int>(info.format), static_cast<int>(info.format),
static_cast<vk::ImageUsageFlags::MaskType>(create.usage)); static_cast<vk::ImageUsageFlags::MaskType>(create.usage), info.samples,
static_cast<vk::SampleCountFlags::MaskType>(properties.sampleCounts));
} }
auto* image = new DepthStencilVulkanImage; auto* image = new DepthStencilVulkanImage;
image->extent.width = info.width; image->extent.width = info.width;
image->extent.height = info.height; image->extent.height = info.height;
image->guest_pitch = info.pitch; image->guest_pitch = info.pitch;
image->layers = info.layers; image->layers = info.layers;
image->samples = info.samples;
image->format = info.format; image->format = info.format;
image->layout = vk::ImageLayout::eUndefined; image->layout = vk::ImageLayout::eUndefined;
image->compressed = false; image->compressed = false;
@@ -326,7 +340,7 @@ void ValidateVideoOut(GraphicContext* ctx, const VideoOutInfo& info) {
" pitch=%u\n", " pitch=%u\n",
info.address, info.size, exact.size, exact.align, info.pitch); info.address, info.size, exact.size, exact.align, info.pitch);
} }
(void)RenderTargetUsage(ctx, info.format, vk::ImageCreateFlags {}); (void)RenderTargetUsage(ctx, info.format, vk::ImageCreateFlags {}, 1);
} }
VideoOutVulkanImage* CreateVideoOut(GraphicContext* ctx, const VideoOutInfo& info) { VideoOutVulkanImage* CreateVideoOut(GraphicContext* ctx, const VideoOutInfo& info) {
@@ -345,7 +359,7 @@ VideoOutVulkanImage* CreateVideoOut(GraphicContext* ctx, const VideoOutInfo& inf
create.tiling = vk::ImageTiling::eOptimal; create.tiling = vk::ImageTiling::eOptimal;
create.initialLayout = vk::ImageLayout::eUndefined; create.initialLayout = vk::ImageLayout::eUndefined;
create.flags = RenderTargetCreateFlags(info.format); create.flags = RenderTargetCreateFlags(info.format);
create.usage = RenderTargetUsage(ctx, info.format, create.flags); create.usage = RenderTargetUsage(ctx, info.format, create.flags, 1);
create.sharingMode = vk::SharingMode::eExclusive; create.sharingMode = vk::SharingMode::eExclusive;
create.samples = vk::SampleCountFlagBits::e1; create.samples = vk::SampleCountFlagBits::e1;
image->memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal; image->memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
+230 -42
View File
@@ -41,6 +41,7 @@ struct RenderTargetInfo {
uint32_t tile_mode = 0; uint32_t tile_mode = 0;
uint32_t levels = 1; uint32_t levels = 1;
uint32_t layers = 1; uint32_t layers = 1;
uint32_t samples = 1;
}; };
// Common image-to-buffer copy description. Storage images and render targets keep distinct // Common image-to-buffer copy description. Storage images and render targets keep distinct
@@ -55,20 +56,21 @@ struct ColorImageTransferInfo {
uint32_t bytes_per_element = 0; uint32_t bytes_per_element = 0;
uint32_t tile_mode = 0; uint32_t tile_mode = 0;
uint32_t levels = 1; uint32_t levels = 1;
uint32_t samples = 1;
}; };
[[nodiscard]] inline ColorImageTransferInfo [[nodiscard]] inline ColorImageTransferInfo
MakeColorImageTransferInfo(const ImageInfo& info, vk::Format format, MakeColorImageTransferInfo(const ImageInfo& info, vk::Format format,
uint32_t bytes_per_element) noexcept { uint32_t bytes_per_element) noexcept {
return {info.address, info.size, format, info.width, info.height, return {info.address, info.size, format, info.width, info.height,
info.pitch, bytes_per_element, info.tile, info.levels}; info.pitch, bytes_per_element, info.tile, info.levels, 1};
} }
[[nodiscard]] inline ColorImageTransferInfo [[nodiscard]] inline ColorImageTransferInfo
MakeColorImageTransferInfo(const RenderTargetInfo& info) noexcept { MakeColorImageTransferInfo(const RenderTargetInfo& info) noexcept {
return { return {
info.address, info.size, info.format, info.width, info.height, info.pitch, info.address, info.size, info.format, info.width, info.height, info.pitch,
info.bytes_per_element, info.tile_mode, info.levels}; info.bytes_per_element, info.tile_mode, info.levels, info.samples};
} }
struct DepthTargetInfo { struct DepthTargetInfo {
@@ -86,7 +88,9 @@ struct DepthTargetInfo {
uint32_t bytes_per_element = 0; uint32_t bytes_per_element = 0;
uint32_t tile_mode = 0; uint32_t tile_mode = 0;
uint32_t layers = 1; uint32_t layers = 1;
uint32_t samples = 1;
bool depth_load_clear = false; bool depth_load_clear = false;
bool depth_access = false;
bool stencil_load_clear = false; bool stencil_load_clear = false;
bool stencil_access = false; bool stencil_access = false;
bool stencil_htile_compressed = false; bool stencil_htile_compressed = false;
@@ -225,6 +229,13 @@ FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
: info.format == policy->depth_attachment_format); : info.format == policy->depth_attachment_format);
} }
[[nodiscard]] inline constexpr bool IsSupportedDepthReadbackFormat(const DepthTargetInfo& info) {
const bool has_stencil = info.stencil_address != 0 || info.stencil_size != 0;
return IsSupportedDepthTargetFormat(info) &&
DepthAspectTransferBytes(info.format) == info.bytes_per_element &&
(!has_stencil || !info.stencil_htile_compressed);
}
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported }; enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
struct VideoOutInfo { struct VideoOutInfo {
@@ -335,12 +346,14 @@ enum class DepthOverlap : uint8_t {
RetireStorage, RetireStorage,
ExpandTarget, ExpandTarget,
DiscardTarget, DiscardTarget,
RecreateTarget,
Unsupported Unsupported
}; };
enum class DepthTransitionSource : uint8_t { None, Guest, Native }; enum class DepthTransitionSource : uint8_t { None, Guest, Native };
enum class RenderTargetOverlap : uint8_t { enum class RenderTargetOverlap : uint8_t {
None, None,
RetireSampled, RetireSampled,
RetireStorage,
PreserveStorage, PreserveStorage,
ExpandTarget, ExpandTarget,
RetireTarget, RetireTarget,
@@ -373,8 +386,24 @@ enum class BufferImageWrite : uint8_t {
Unsupported Unsupported
}; };
[[nodiscard]] inline constexpr bool
HasGuestCurrentImageOwnership(bool image_gpu_modified, bool buffer_modified, bool cpu_dirty,
bool tracker_gpu_modified, bool same_context) noexcept {
return !image_gpu_modified && !buffer_modified && !cpu_dirty && !tracker_gpu_modified &&
same_context;
}
enum class StorageBufferRebind : uint8_t { Reuse, RefreshFromBacking, Unsupported }; enum class StorageBufferRebind : uint8_t { Reuse, RefreshFromBacking, Unsupported };
enum class MetaImageOverlap : uint8_t { RetainSampled, RetireTarget, Unsupported }; enum class MetaImageOverlap : uint8_t { RetainSampled, RetireImage, Unsupported };
[[nodiscard]] inline constexpr bool CanRetireGuestCurrentDepthForMetadataReuse(
bool depth_gpu_modified, bool depth_buffer_modified, bool depth_tracker_gpu_modified,
bool metadata_gpu_modified, bool metadata_tracker_gpu_modified, uint32_t metadata_clear_mask,
bool same_context) noexcept {
return !metadata_gpu_modified && !metadata_tracker_gpu_modified && metadata_clear_mask == 0 &&
HasGuestCurrentImageOwnership(depth_gpu_modified, depth_buffer_modified, false,
depth_tracker_gpu_modified, same_context);
}
[[nodiscard]] inline constexpr uint32_t SelectImageBackingBaseLevel(bool storage, [[nodiscard]] inline constexpr uint32_t SelectImageBackingBaseLevel(bool storage,
uint32_t view_base_level) { uint32_t view_base_level) {
@@ -434,7 +463,7 @@ IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
IsSupportedStandard64RenderTarget(const RenderTargetInfo& info) noexcept { IsSupportedStandard64RenderTarget(const RenderTargetInfo& info) noexcept {
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) || if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
info.address == 0 || (info.address & 0xffffu) != 0 || info.width == 0 || info.height == 0 || info.address == 0 || (info.address & 0xffffu) != 0 || info.width == 0 || info.height == 0 ||
info.bytes_per_element != 4 || info.levels != 1 || info.layers != 1) { info.bytes_per_element != 4 || info.levels != 1 || info.layers != 1 || info.samples != 1) {
return false; return false;
} }
const auto expected_pitch = (static_cast<uint64_t>(info.width) + 127u) & ~uint64_t {127u}; const auto expected_pitch = (static_cast<uint64_t>(info.width) + 127u) & ~uint64_t {127u};
@@ -462,14 +491,15 @@ SelectDepthTransitionSource(bool depth_load_clear, bool sampled_native_available
: DepthTransitionSource::Guest; : DepthTransitionSource::Guest;
} }
[[nodiscard]] inline MetaImageOverlap ClassifyMetaImageOverlap(bool sampled, bool render_target, [[nodiscard]] inline MetaImageOverlap ClassifyMetaImageOverlap(bool sampled, bool writable_image,
bool gpu_modified, bool gpu_modified,
bool buffer_modified) { bool buffer_modified, bool cpu_dirty,
bool same_context) {
if (sampled && !gpu_modified) { if (sampled && !gpu_modified) {
return MetaImageOverlap::RetainSampled; return MetaImageOverlap::RetainSampled;
} }
if (render_target && !gpu_modified && !buffer_modified) { if (writable_image && !gpu_modified && !buffer_modified && !cpu_dirty && same_context) {
return MetaImageOverlap::RetireTarget; return MetaImageOverlap::RetireImage;
} }
return MetaImageOverlap::Unsupported; return MetaImageOverlap::Unsupported;
} }
@@ -559,6 +589,7 @@ SelectDepthTransitionSource(bool depth_load_clear, bool sampled_native_available
const bool d32 = const bool d32 =
target.format == vk::Format::eD32Sfloat || target.format == vk::Format::eD32SfloatS8Uint; target.format == vk::Format::eD32Sfloat || target.format == vk::Format::eD32SfloatS8Uint;
return address == target.address && size == target.size && target.layers == 1 && d32 && return address == target.address && size == target.size && target.layers == 1 && d32 &&
target.samples == 1 &&
target.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) && target.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) &&
target.bytes_per_element == 4 && target.bytes_per_element == 4 &&
target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
@@ -826,8 +857,11 @@ ClassifyStorageBufferRebind(bool buffer_overlap, bool cached_gpu_modified,
// Mirrors storage -> depth-target binding transition. An inaccessible stencil aspect // Mirrors storage -> depth-target binding transition. An inaccessible stencil aspect
// does not require the otherwise necessary image content copy. // does not require the otherwise necessary image content copy.
[[nodiscard]] inline DepthOverlap ClassifyStorageDepthOverlap(const ImageInfo& storage, [[nodiscard]] inline DepthOverlap
const DepthTargetInfo& depth) { ClassifyStorageDepthOverlap(const ImageInfo& storage, bool storage_gpu_modified,
bool storage_buffer_modified, bool storage_cpu_dirty,
bool tracker_gpu_modified, bool same_context,
const DepthTargetInfo& depth) {
const bool overlaps_depth = const bool overlaps_depth =
ImageRangeOverlaps(storage.address, storage.size, depth.address, depth.size); ImageRangeOverlaps(storage.address, storage.size, depth.address, depth.size);
const bool overlaps_stencil = const bool overlaps_stencil =
@@ -836,11 +870,47 @@ ClassifyStorageBufferRebind(bool buffer_overlap, bool cached_gpu_modified,
if (!overlaps_depth && !overlaps_stencil) { if (!overlaps_depth && !overlaps_stencil) {
return DepthOverlap::None; return DepthOverlap::None;
} }
return !overlaps_depth && overlaps_stencil && !depth.stencil_access const bool guest_current =
HasGuestCurrentImageOwnership(storage_gpu_modified, storage_buffer_modified,
storage_cpu_dirty, tracker_gpu_modified, same_context);
const bool aspects_discarded =
(!overlaps_depth || !depth.depth_access) && (!overlaps_stencil || !depth.stencil_access);
return guest_current || (!storage_buffer_modified && !storage_cpu_dirty && same_context &&
aspects_discarded)
? DepthOverlap::RetireStorage ? DepthOverlap::RetireStorage
: DepthOverlap::Unsupported; : DepthOverlap::Unsupported;
} }
[[nodiscard]] inline bool CanRetireGuestCurrentDepthForSampled(
const ImageInfo& sampled, const DepthTargetInfo& depth, bool depth_gpu_modified,
bool depth_buffer_modified, bool depth_tracker_gpu_modified, bool stencil_tracker_gpu_modified,
bool same_context) noexcept {
const bool overlaps_depth =
ImageRangeOverlaps(sampled.address, sampled.size, depth.address, depth.size);
const bool overlaps_stencil =
depth.stencil_address != 0 && ImageRangeOverlaps(sampled.address, sampled.size,
depth.stencil_address, depth.stencil_size);
return (overlaps_depth || overlaps_stencil) &&
HasGuestCurrentImageOwnership(depth_gpu_modified, depth_buffer_modified, false,
depth_tracker_gpu_modified || stencil_tracker_gpu_modified,
same_context);
}
[[nodiscard]] inline bool
CanRetireGuestCurrentSampledForDepth(const ImageInfo& sampled, const DepthTargetInfo& depth,
bool sampled_gpu_modified, bool sampled_buffer_modified,
bool sampled_cpu_dirty, bool tracker_gpu_modified,
bool same_context, bool guest_source_current) noexcept {
const bool overlaps_depth =
ImageRangeOverlaps(sampled.address, sampled.size, depth.address, depth.size);
const bool overlaps_stencil =
depth.stencil_address != 0 && ImageRangeOverlaps(sampled.address, sampled.size,
depth.stencil_address, depth.stencil_size);
return (overlaps_depth || overlaps_stencil) && guest_source_current &&
HasGuestCurrentImageOwnership(sampled_gpu_modified, sampled_buffer_modified,
sampled_cpu_dirty, tracker_gpu_modified, same_context);
}
[[nodiscard]] inline RenderTargetOverlap [[nodiscard]] inline RenderTargetOverlap
ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified, bool same_context, ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified, bool same_context,
const RenderTargetInfo& target) { const RenderTargetInfo& target) {
@@ -857,11 +927,14 @@ ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified,
[[nodiscard]] inline RenderTargetOverlap [[nodiscard]] inline RenderTargetOverlap
ClassifyStorageRenderTargetOverlap(const ImageInfo& storage, vk::Format storage_format, ClassifyStorageRenderTargetOverlap(const ImageInfo& storage, vk::Format storage_format,
bool storage_gpu_modified, bool storage_buffer_modified, bool storage_gpu_modified, bool storage_buffer_modified,
bool storage_cpu_dirty, bool same_context, bool storage_cpu_dirty, bool tracker_gpu_modified,
const RenderTargetInfo& target) { bool same_context, const RenderTargetInfo& target) {
if (!ImagePageRangesOverlap(storage.address, storage.size, target.address, target.size)) { if (!ImagePageRangesOverlap(storage.address, storage.size, target.address, target.size)) {
return RenderTargetOverlap::None; return RenderTargetOverlap::None;
} }
if (!ImageRangeOverlaps(storage.address, storage.size, target.address, target.size)) {
return RenderTargetOverlap::None;
}
const bool exact_native_image = const bool exact_native_image =
storage.address == target.address && storage.size == target.size && storage.address == target.address && storage.size == target.size &&
storage_format == target.format && storage.width == target.width && storage_format == target.format && storage.width == target.width &&
@@ -869,10 +942,14 @@ ClassifyStorageRenderTargetOverlap(const ImageInfo& storage, vk::Format storage_
storage.base_level == 0 && storage.levels == 1 && storage.view_levels == 1 && storage.base_level == 0 && storage.levels == 1 && storage.view_levels == 1 &&
storage.tile == target.tile_mode && storage.depth == 1 && storage.tile == target.tile_mode && storage.depth == 1 &&
storage.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) && storage.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
storage.base_array == 0 && target.levels == 1 && target.layers == 1; storage.base_array == 0 && target.levels == 1 && target.layers == 1 && target.samples == 1;
return exact_native_image && storage_gpu_modified && !storage_buffer_modified && if (exact_native_image && storage_gpu_modified && tracker_gpu_modified &&
!storage_cpu_dirty && same_context !storage_buffer_modified && !storage_cpu_dirty && same_context) {
? RenderTargetOverlap::PreserveStorage return RenderTargetOverlap::PreserveStorage;
}
return HasGuestCurrentImageOwnership(storage_gpu_modified, storage_buffer_modified,
storage_cpu_dirty, tracker_gpu_modified, same_context)
? RenderTargetOverlap::RetireStorage
: RenderTargetOverlap::Unsupported; : RenderTargetOverlap::Unsupported;
} }
@@ -899,7 +976,7 @@ ClassifyStorageRenderTargetOverlap(const ImageInfo& storage, vk::Format storage_
cached.pitch == requested.pitch && cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element && cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.levels == requested.levels && cached.tile_mode == requested.tile_mode && cached.levels == requested.levels &&
cached.layers == requested.layers && cached.layers == requested.layers && cached.samples == requested.samples &&
IsRgba8SrgbReinterpretation(cached.format, requested.format); IsRgba8SrgbReinterpretation(cached.format, requested.format);
} }
@@ -959,6 +1036,7 @@ IsCompatibleRenderTargetBacking(const RenderTargetInfo& cached,
cached.pitch == requested.pitch && cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element && cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.levels == requested.levels && cached.tile_mode == requested.tile_mode && cached.levels == requested.levels &&
cached.samples == requested.samples &&
(cached.format == requested.format || (cached.format == requested.format ||
IsRgba8SrgbReinterpretation(cached.format, requested.format)); IsRgba8SrgbReinterpretation(cached.format, requested.format));
} }
@@ -977,34 +1055,46 @@ IsCompatibleDepthTargetBacking(const DepthTargetInfo& cached,
cached.width == requested.width && cached.height == requested.height && cached.width == requested.width && cached.height == requested.height &&
cached.pitch == requested.pitch && cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element && cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.tile_mode == requested.tile_mode && cached.samples == requested.samples &&
cached.stencil_htile_compressed == requested.stencil_htile_compressed; cached.stencil_htile_compressed == requested.stencil_htile_compressed;
} }
[[nodiscard]] inline RenderTargetOverlap [[nodiscard]] inline RenderTargetOverlap
ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modified, ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modified,
bool cached_buffer_modified, bool same_context, bool cached_buffer_modified, bool tracker_gpu_modified,
bool same_context, bool guest_source_current,
const RenderTargetInfo& requested) { const RenderTargetInfo& requested) {
if (!ImagePageRangesOverlap(cached.address, cached.size, requested.address, requested.size)) { if (!ImagePageRangesOverlap(cached.address, cached.size, requested.address, requested.size)) {
return RenderTargetOverlap::None; return RenderTargetOverlap::None;
} }
if (!ImageRangeOverlaps(cached.address, cached.size, requested.address, requested.size)) {
return RenderTargetOverlap::None;
}
const bool expand = requested.layers > cached.layers && const bool expand = requested.layers > cached.layers &&
IsCompatibleRenderTargetBacking(requested, cached) && IsCompatibleRenderTargetBacking(requested, cached) &&
cached.format == requested.format; cached.format == requested.format;
if (expand && cached_gpu_modified && !cached_buffer_modified && same_context) { if (expand && cached_gpu_modified && tracker_gpu_modified && !cached_buffer_modified &&
same_context) {
return RenderTargetOverlap::ExpandTarget; return RenderTargetOverlap::ExpandTarget;
} }
// For an equal-address allocation-pool entry, a changed block raster or block size is a new // A clean, page-isolated overlap is allocation-pool reuse, including a contained subrange.
// image allocation, not a view of the old image. In Kyty we can only retire an // Compatible views are handled before this classifier and are never retired here.
// already-published target with page-isolated storage.
const bool page_isolated = const bool page_isolated =
cached.address % TRACKER_PAGE_SIZE == 0 && cached.size % TRACKER_PAGE_SIZE == 0 && cached.address % TRACKER_PAGE_SIZE == 0 && cached.size % TRACKER_PAGE_SIZE == 0 &&
requested.address % TRACKER_PAGE_SIZE == 0 && requested.size % TRACKER_PAGE_SIZE == 0; requested.address % TRACKER_PAGE_SIZE == 0 && requested.size % TRACKER_PAGE_SIZE == 0;
const bool pool_storage_shape_changed = cached.pitch != requested.pitch || const bool incompatible_format = cached.format != requested.format &&
cached.height != requested.height || !IsRgba8SrgbReinterpretation(cached.format, requested.format);
cached.bytes_per_element != requested.bytes_per_element; const bool pool_storage_shape_changed =
return cached.address == requested.address && page_isolated && pool_storage_shape_changed && incompatible_format || cached.width != requested.width ||
!cached_gpu_modified && !cached_buffer_modified && same_context cached.height != requested.height || cached.pitch != requested.pitch ||
cached.bytes_per_element != requested.bytes_per_element ||
cached.tile_mode != requested.tile_mode || cached.levels != requested.levels ||
cached.layers != requested.layers || cached.samples != requested.samples;
const bool new_allocation = cached.address != requested.address ||
cached.size != requested.size || pool_storage_shape_changed;
return page_isolated && new_allocation && guest_source_current &&
HasGuestCurrentImageOwnership(cached_gpu_modified, cached_buffer_modified, false,
tracker_gpu_modified, same_context)
? RenderTargetOverlap::RetireTarget ? RenderTargetOverlap::RetireTarget
: RenderTargetOverlap::Unsupported; : RenderTargetOverlap::Unsupported;
} }
@@ -1014,7 +1104,24 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi
bool cached_buffer_modified, bool cached_buffer_modified,
bool same_context, bool same_context,
const DepthTargetInfo& requested) { const DepthTargetInfo& requested) {
if (!ImagePageRangesOverlap(cached.address, cached.size, requested.address, requested.size)) { const auto overlaps = [](uint64_t left, uint64_t left_size, uint64_t right, uint64_t right_size,
bool pages) {
if (left == 0 || left_size == 0 || right == 0 || right_size == 0) {
return false;
}
return pages ? ImagePageRangesOverlap(left, left_size, right, right_size)
: ImageRangeOverlaps(left, left_size, right, right_size);
};
const auto planes_overlap = [&](bool pages) {
return overlaps(cached.address, cached.size, requested.address, requested.size, pages) ||
overlaps(cached.address, cached.size, requested.stencil_address,
requested.stencil_size, pages) ||
overlaps(cached.stencil_address, cached.stencil_size, requested.address,
requested.size, pages) ||
overlaps(cached.stencil_address, cached.stencil_size, requested.stencil_address,
requested.stencil_size, pages);
};
if (!planes_overlap(true) || !planes_overlap(false)) {
return DepthOverlap::None; return DepthOverlap::None;
} }
const bool expand = const bool expand =
@@ -1025,19 +1132,100 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi
const bool exact_discard = const bool exact_discard =
requested.depth_load_clear && cached_gpu_modified && !cached_buffer_modified && requested.depth_load_clear && cached_gpu_modified && !cached_buffer_modified &&
same_context && cached.address == requested.address && cached.size == requested.size && same_context && cached.address == requested.address && cached.size == requested.size &&
cached.stencil_address == 0 && cached.stencil_size == 0 && cached.htile_address == 0 && cached.samples == requested.samples && cached.stencil_address == 0 &&
cached.htile_size == 0 && requested.stencil_address == 0 && requested.stencil_size == 0 && cached.stencil_size == 0 && cached.htile_address == 0 && cached.htile_size == 0 &&
requested.stencil_address == 0 && requested.stencil_size == 0 &&
requested.htile_address == 0 && requested.htile_size == 0; requested.htile_address == 0 && requested.htile_size == 0;
return exact_discard ? DepthOverlap::DiscardTarget : DepthOverlap::Unsupported; if (exact_discard) {
return DepthOverlap::DiscardTarget;
}
const bool exact_plane_rebind =
(cached.address == requested.address && cached.size == requested.size) ||
(cached.address == requested.stencil_address && cached.size == requested.stencil_size) ||
(cached.stencil_address == requested.address && cached.stencil_size == requested.size) ||
(cached.stencil_address == requested.stencil_address &&
cached.stencil_size == requested.stencil_size);
const bool same_shape = cached.format == requested.format &&
cached.guest_format == requested.guest_format &&
cached.width == requested.width && cached.height == requested.height &&
cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.layers == 1 &&
requested.layers == 1 && cached.samples == 1 && requested.samples == 1;
return exact_plane_rebind && same_shape && !cached_buffer_modified && same_context
? DepthOverlap::RecreateTarget
: DepthOverlap::Unsupported;
} }
[[nodiscard]] inline bool CanRetireBufferOwnedDepthForRenderTarget( [[nodiscard]] inline bool CanRecreateDepthForRenderTarget(const DepthTargetInfo& depth,
const DepthTargetInfo& depth, bool gpu_modified, bool buffer_modified, bool same_context, bool gpu_modified, bool buffer_modified,
bool containing_buffer_source, const RenderTargetInfo& target) noexcept { bool tracker_gpu_modified,
return !gpu_modified && buffer_modified && same_context && containing_buffer_source && bool same_context,
depth.address == target.address && depth.size == target.size && bool guest_source_current,
depth.stencil_address == 0 && depth.stencil_size == 0 && depth.htile_address == 0 && const RenderTargetInfo& target) noexcept {
depth.htile_size == 0; const bool exact_depth = depth.address == target.address && depth.size == target.size;
const bool exact_stencil =
depth.stencil_address == target.address && depth.stencil_size == target.size;
const auto contains = [&](uint64_t address, uint64_t size) {
if (address == 0 || size == 0 || address < target.address) {
return false;
}
const auto offset = address - target.address;
return offset <= target.size && size <= target.size - offset;
};
const bool page_isolated_rebind =
target.address % TRACKER_PAGE_SIZE == 0 && target.size % TRACKER_PAGE_SIZE == 0 &&
((contains(depth.address, depth.size) && depth.address % TRACKER_PAGE_SIZE == 0 &&
depth.size % TRACKER_PAGE_SIZE == 0) ||
(contains(depth.stencil_address, depth.stencil_size) &&
depth.stencil_address % TRACKER_PAGE_SIZE == 0 &&
depth.stencil_size % TRACKER_PAGE_SIZE == 0));
const bool ownership_consistent = gpu_modified == tracker_gpu_modified && !buffer_modified;
const bool source_available =
((exact_depth || exact_stencil) && gpu_modified) || guest_source_current;
return same_context && ownership_consistent && source_available &&
(exact_depth || exact_stencil || page_isolated_rebind) && depth.layers == 1 &&
target.levels == 1 && target.layers == 1 && depth.samples == 1 && target.samples == 1;
}
[[nodiscard]] inline bool
CanRecreateRenderTargetForDepth(const RenderTargetInfo& target, bool gpu_modified,
bool buffer_modified, bool tracker_gpu_modified, bool same_context,
bool guest_source_current, const DepthTargetInfo& depth) noexcept {
const bool exact_depth = target.address == depth.address && target.size == depth.size;
const bool exact_stencil =
target.address == depth.stencil_address && target.size == depth.stencil_size;
const auto overlaps_plane = [&](uint64_t address, uint64_t size) {
return address != 0 && size != 0 &&
ImageRangeOverlaps(target.address, target.size, address, size);
};
const bool page_isolated_rebind =
target.address % TRACKER_PAGE_SIZE == 0 && target.size % TRACKER_PAGE_SIZE == 0 &&
((overlaps_plane(depth.address, depth.size) && depth.address % TRACKER_PAGE_SIZE == 0 &&
depth.size % TRACKER_PAGE_SIZE == 0) ||
(overlaps_plane(depth.stencil_address, depth.stencil_size) &&
depth.stencil_address % TRACKER_PAGE_SIZE == 0 &&
depth.stencil_size % TRACKER_PAGE_SIZE == 0));
const bool source_available =
(gpu_modified && (exact_depth || exact_stencil)) || guest_source_current;
return gpu_modified == tracker_gpu_modified && !buffer_modified && same_context &&
source_available && (exact_depth || exact_stencil || page_isolated_rebind) &&
target.levels == 1 && target.layers == 1 && depth.layers == 1 && target.samples == 1 &&
depth.samples == 1;
}
[[nodiscard]] inline bool RequiresMultisampleDepthRefresh(const DepthTargetInfo& info,
bool buffer_modified,
bool depth_cpu_modified,
bool stencil_cpu_modified) noexcept {
if (info.samples == 1) {
return false;
}
const bool depth_refresh =
info.depth_access && !info.depth_load_clear && (buffer_modified || depth_cpu_modified);
const bool stencil_refresh = info.stencil_access && !info.stencil_load_clear &&
(buffer_modified || stencil_cpu_modified);
return depth_refresh || stencil_refresh;
} }
} // namespace Libs::Graphics } // namespace Libs::Graphics
+1 -1
View File
@@ -94,7 +94,7 @@ void CreateRenderTargetViews(GraphicContext* ctx, RenderTextureVulkanImage* imag
vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity,
vk::ImageViewType::e2DArray); vk::ImageViewType::e2DArray);
} }
if (FormatSupportsStorage(ctx, image->format)) { if (image->samples == 1 && FormatSupportsStorage(ctx, image->format)) {
CreateRenderTargetView(ctx, image, VulkanImage::VIEW_STORAGE, CreateRenderTargetView(ctx, image, VulkanImage::VIEW_STORAGE,
vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity,
vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity, vk::ComponentSwizzle::eIdentity,
@@ -8,6 +8,7 @@
#include "graphics/host_gpu/renderer/debug.h" #include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h" #include "graphics/host_gpu/renderer/depthRenderTarget.h"
#include "graphics/host_gpu/renderer/framebufferCache.h" #include "graphics/host_gpu/renderer/framebufferCache.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
@@ -89,6 +90,13 @@ PipelineCache::GraphicsPipeline* PipelineCache::CreateGraphicsPipeline(
static_params.negative_one_to_one = !ctx->GetClipControl().dx_clip_space; static_params.negative_one_to_one = !ctx->GetClipControl().dx_clip_space;
static_params.topology = topology; static_params.topology = topology;
static_params.samples = framebuffer->samples;
static_params.sample_shading_enable =
ps_active && framebuffer->samples > 1 && ps_input_info->ps_sample_shading;
if (static_params.sample_shading_enable &&
!g_render_ctx->GetGraphicCtx()->sample_rate_shading_enabled) {
EXIT("Pipeline: sample-rate shading is required but unsupported by the host\n");
}
static_params.with_depth = static_params.with_depth =
(depth->format != vk::Format::eUndefined && depth->vulkan_buffer != nullptr); (depth->format != vk::Format::eUndefined && depth->vulkan_buffer != nullptr);
static_params.depth_test_enable = depth->depth_test_enable; static_params.depth_test_enable = depth->depth_test_enable;
@@ -35,6 +35,8 @@ struct PipelineStaticParameters {
bool negative_one_to_one = false; bool negative_one_to_one = false;
int scissor_ltrb[4] = {0}; int scissor_ltrb[4] = {0};
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
uint32_t samples = 1;
bool sample_shading_enable = false;
bool with_depth = false; bool with_depth = false;
bool depth_test_enable = false; bool depth_test_enable = false;
bool depth_write_enable = false; bool depth_write_enable = false;
@@ -74,8 +76,8 @@ static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
static_assert(alignof(PipelineStaticParameters) == 1); static_assert(alignof(PipelineStaticParameters) == 1);
static_assert(sizeof(PipelineStaticParameters) == static_assert(sizeof(PipelineStaticParameters) ==
sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) + sizeof(int[4]) + sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) + sizeof(int[4]) +
sizeof(vk::PrimitiveTopology) + sizeof(bool) * 3 + sizeof(vk::CompareOp) + sizeof(vk::PrimitiveTopology) + sizeof(uint32_t) + sizeof(bool) * 4 +
sizeof(bool) + sizeof(float) * 2 + sizeof(bool) + sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 + sizeof(bool) +
sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) +
sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 + sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 +
sizeof(uint8_t[RENDER_COLOR_ATTACHMENTS_MAX]) * 6 + sizeof(uint8_t[RENDER_COLOR_ATTACHMENTS_MAX]) * 6 +
@@ -59,18 +59,16 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor
const bool has_stencil = const bool has_stencil =
z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid); z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid);
const auto* depth_policy = FindDepthFormatPolicy(z.z_info.format); const auto* depth_policy = FindDepthFormatPolicy(z.z_info.format);
const bool msaa_compat = depth_msaa_single_sample_compatible(z.z_info.num_samples);
const bool supported_depth_state = const bool supported_depth_state =
z.z_info.tile_surface_enable && depth_policy != nullptr && z.z_info.tile_mode_index == 0 && z.z_info.tile_surface_enable && depth_policy != nullptr && z.z_info.tile_mode_index == 0 &&
(z.z_info.num_samples == 0 || msaa_compat) && z.z_info.zrange_precision <= 1 && z.z_info.num_samples <= 3 && z.z_info.zrange_precision <= 1 && !z.z_info.expclear_enabled &&
!z.z_info.expclear_enabled && !z.z_info.embedded_sample_locations && !z.z_info.embedded_sample_locations && !z.z_info.partially_resident &&
!z.z_info.partially_resident && z.z_info.num_mip_levels == 0 && z.z_info.num_mip_levels == 0 && z.z_info.plane_compression == 0 &&
z.z_info.plane_compression == 0 && z.depth_view.current_mip_level == 0 && z.depth_view.current_mip_level == 0 && z.depth_view.slice_start == 0 &&
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 && z.depth_view.slice_max == 0 && z.depth_info.addr5_swizzle_mask == 0 &&
z.depth_info.addr5_swizzle_mask == 0 && z.depth_info.array_mode == 0 && z.depth_info.array_mode == 0 && z.depth_info.pipe_config == 0 &&
z.depth_info.pipe_config == 0 && z.depth_info.bank_width == 0 && z.depth_info.bank_width == 0 && z.depth_info.bank_height == 0 &&
z.depth_info.bank_height == 0 && z.depth_info.macro_tile_aspect == 0 && z.depth_info.macro_tile_aspect == 0 && z.depth_info.num_banks == 0;
z.depth_info.num_banks == 0;
const bool supported_stencil_state = const bool supported_stencil_state =
z.stencil_info.tile_mode_index == 0 && z.stencil_info.tile_split == 0 && z.stencil_info.tile_mode_index == 0 && z.stencil_info.tile_split == 0 &&
!z.stencil_info.expclear_enabled && !z.stencil_info.expclear_enabled &&
@@ -100,14 +98,6 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor
!supported_addresses) { !supported_addresses) {
return false; return false;
} }
if (msaa_compat) {
static std::atomic<uint32_t> logged_fragments = 0;
const uint32_t bit = 1u << z.z_info.num_samples;
if ((logged_fragments.fetch_or(bit, std::memory_order_relaxed) & bit) == 0) {
LOGF("HTileClear: compatibility: treating PS5 %ux depth fragments as single-sample\n",
bit);
}
}
const bool size_xy_valid = z.size.valid; const bool size_xy_valid = z.size.valid;
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0; const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
@@ -133,36 +123,8 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor
(z.pitch_div8_minus1 != 0 || z.height_div8_minus1 != 0 || z.slice_div64_minus1 != 0))) { (z.pitch_div8_minus1 != 0 || z.height_div8_minus1 != 0 || z.slice_div64_minus1 != 0))) {
return false; return false;
} }
TileSizeAlign htile_size {};
const uint32_t guest_format = Prospero::GpuEnumValue(depth_policy->guest_format); if (!TileGetHtileSize(width, height, &htile_size) || htile_size.size != descriptor_size) {
const uint32_t bytes = depth_policy->bytes_per_element;
const uint32_t pitch = TileGetTexturePitch(guest_format, width, 1,
Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
(static_cast<uint64_t>(z.height_div8_minus1) + 1u) * 8u !=
((static_cast<uint64_t>(height) + 7u) & ~7ull))) {
return false;
}
const uint32_t block_width = bytes == 2 ? 256u : 128u;
const uint64_t padded_width =
(static_cast<uint64_t>(pitch) + block_width - 1u) & ~(block_width - 1u);
const uint64_t padded_height = (static_cast<uint64_t>(height) + 127u) & ~127ull;
if (padded_width > UINT64_MAX / padded_height ||
padded_width * padded_height > UINT64_MAX / bytes) {
return false;
}
const uint64_t expected_depth_size = padded_width * padded_height * bytes;
TileSizeAlign depth_size {};
TileSizeAlign stencil_size {};
TileSizeAlign htile_size {};
if (!TileGetDepthSize(width, height, 0, z.z_info.format, z.stencil_info.format, true,
&stencil_size, &htile_size, &depth_size) ||
expected_depth_size == 0 || expected_depth_size > UINT32_MAX || depth_size.align != 65536 ||
depth_size.size != expected_depth_size ||
(has_stencil != (stencil_size.align == 65536 && stencil_size.size != 0)) ||
htile_size.align != 32768 || htile_size.size == 0 || htile_size.size != descriptor_size ||
(z.pitch_height_valid &&
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u != expected_depth_size)) {
return false; return false;
} }
*resolved = {.address = z.htile_data_base_addr, .size = htile_size.size}; *resolved = {.address = z.htile_data_base_addr, .size = htile_size.size};
@@ -232,9 +194,19 @@ static bool TryConsumeComputeMetaClear(const ShaderComputeInputInfo& input, cons
HtileClearTarget target {}; HtileClearTarget target {};
if (current_references != 0) { if (current_references != 0) {
if (!ResolveHtileClearTarget(z, described_meta_size, &target)) { if (!ResolveHtileClearTarget(z, described_meta_size, &target)) {
EXIT("unsupported HTile compute-clear target state\n"); EXIT("unsupported HTile compute-clear target state: current=%u registered=%u "
"meta=0x%016" PRIx64 "+0x%016" PRIx64 " depth=0x%016" PRIx64 "/0x%016" PRIx64
" stencil=0x%016" PRIx64 "/0x%016" PRIx64
" extent=%d:%ux%u wh=%d:%ux%u pitch=%d:%u/%u/%u zfmt=%u sfmt=%u samples=%u\n",
current_references, registered_writes, meta_addr, described_meta_size,
z.z_read_base_addr, z.z_write_base_addr, z.stencil_read_base_addr,
z.stencil_write_base_addr, z.size.valid, static_cast<uint32_t>(z.size.x_max) + 1u,
static_cast<uint32_t>(z.size.y_max) + 1u, z.width_height_valid, z.width, z.height,
z.pitch_height_valid, z.pitch_div8_minus1, z.height_div8_minus1,
z.slice_div64_minus1, z.z_info.format, z.stencil_info.format,
z.z_info.num_samples);
} }
cache->RegisterMeta(target.address, target.size); cache->RegisterMeta(g_render_ctx->GetGraphicCtx(), target.address, target.size);
} else { } else {
target = registered_target; target = registered_target;
} }
+10 -6
View File
@@ -10,14 +10,18 @@ namespace Libs::Graphics {
static constexpr uint32_t RENDER_COLOR_ATTACHMENTS_MAX = 8; static constexpr uint32_t RENDER_COLOR_ATTACHMENTS_MAX = 8;
inline constexpr bool depth_msaa_single_sample_compatible(uint32_t encoded_fragments) { [[nodiscard]] inline constexpr uint32_t render_sample_count(uint32_t encoded_samples) {
return encoded_fragments == 1 || encoded_fragments == 2; return encoded_samples <= 3 ? 1u << encoded_samples : 0;
} }
inline constexpr bool color_msaa_single_sample_compatible(uint32_t encoded_samples, [[nodiscard]] inline constexpr vk::SampleCountFlagBits vulkan_sample_count(uint32_t samples) {
uint32_t encoded_fragments) { switch (samples) {
return encoded_samples == encoded_fragments && case 1: return vk::SampleCountFlagBits::e1;
depth_msaa_single_sample_compatible(encoded_fragments); case 2: return vk::SampleCountFlagBits::e2;
case 4: return vk::SampleCountFlagBits::e4;
case 8: return vk::SampleCountFlagBits::e8;
default: return {};
}
} }
enum class TargetViewType : uint8_t { Image2D, Image2DArray, Unsupported }; enum class TargetViewType : uint8_t { Image2D, Image2DArray, Unsupported };
+2 -2
View File
@@ -755,8 +755,8 @@ void CreatePipelineInternal(PipelineCache::GraphicsPipeline* pipeline, vk::Rende
multisampling.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo; multisampling.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
multisampling.pNext = nullptr; multisampling.pNext = nullptr;
multisampling.flags = {}; multisampling.flags = {};
multisampling.sampleShadingEnable = VK_FALSE; multisampling.sampleShadingEnable = static_params.sample_shading_enable ? VK_TRUE : VK_FALSE;
multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; multisampling.rasterizationSamples = vulkan_sample_count(static_params.samples);
multisampling.minSampleShading = 1.0f; multisampling.minSampleShading = 1.0f;
multisampling.pSampleMask = nullptr; multisampling.pSampleMask = nullptr;
multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToCoverageEnable = VK_FALSE;
File diff suppressed because it is too large Load Diff
+15 -10
View File
@@ -101,15 +101,15 @@ public:
FindDepthTargetByRange(CommandBuffer* command, uint64_t vaddr, uint64_t size, FindDepthTargetByRange(CommandBuffer* command, uint64_t vaddr, uint64_t size,
bool allow_containing_sampled = false); bool allow_containing_sampled = false);
[[nodiscard]] RegionInfo QueryRegion(uint64_t vaddr, uint64_t size); [[nodiscard]] RegionInfo QueryRegion(uint64_t vaddr, uint64_t size);
void RegisterMeta(uint64_t vaddr, uint64_t size, uint32_t layers = 1); void RegisterMeta(GraphicContext* ctx, uint64_t vaddr, uint64_t size, uint32_t layers = 1);
[[nodiscard]] bool IsMeta(uint64_t vaddr); [[nodiscard]] bool IsMeta(uint64_t vaddr);
[[nodiscard]] bool IsMetaRange(uint64_t vaddr, uint64_t size); [[nodiscard]] bool IsMetaRange(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMetaCleared(uint64_t vaddr, uint32_t slice); [[nodiscard]] bool IsMetaCleared(uint64_t vaddr, uint32_t slice);
[[nodiscard]] bool ClearMeta(uint64_t vaddr); [[nodiscard]] bool ClearMeta(uint64_t vaddr);
[[nodiscard]] bool TouchMeta(uint64_t vaddr, uint32_t slice, bool is_clear); [[nodiscard]] bool TouchMeta(uint64_t vaddr, uint32_t slice, bool is_clear);
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size, [[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept; PageFaultPhase phase) noexcept;
void UnmapMemory(uint64_t vaddr, uint64_t size); void UnmapMemory(uint64_t vaddr, uint64_t size);
VulkanImage* GetDummySampledTexture(bool uint_format, bool image_3d); VulkanImage* GetDummySampledTexture(bool uint_format, bool image_3d);
VulkanImage* GetDummyStorageTexture(bool uint_format, bool image_3d); VulkanImage* GetDummyStorageTexture(bool uint_format, bool image_3d);
@@ -129,7 +129,8 @@ private:
[[nodiscard]] bool HasMetaOverlapLocked(uint64_t vaddr, uint64_t size) const; [[nodiscard]] bool HasMetaOverlapLocked(uint64_t vaddr, uint64_t size) const;
[[nodiscard]] CachedImage* FindGpuReadbackPageCandidateLocked(uint64_t vaddr, uint64_t size); [[nodiscard]] CachedImage* FindGpuReadbackPageCandidateLocked(uint64_t vaddr, uint64_t size);
void RequireNoMetaOverlapLocked(uint64_t vaddr, uint64_t size) const; void RequireNoMetaOverlapLocked(uint64_t vaddr, uint64_t size) const;
void MarkSampledAliasesCpuDirtyLocked(uint64_t vaddr, uint64_t size); void ResolveImageMetadataOverlapsLocked(GraphicContext* ctx, uint64_t vaddr, uint64_t size);
void MarkSampledAliasesCpuDirtyLocked(uint64_t vaddr, uint64_t size);
void RetireSampledTargetAliases(GraphicContext* ctx, const ImageInfo& requested); void RetireSampledTargetAliases(GraphicContext* ctx, const ImageInfo& requested);
void ResolveStorageImageOverlaps(GraphicContext* ctx, const ImageInfo& requested); void ResolveStorageImageOverlaps(GraphicContext* ctx, const ImageInfo& requested);
void RetireStorageDepthAliasLocked(GraphicContext* ctx, const ImageInfo& requested); void RetireStorageDepthAliasLocked(GraphicContext* ctx, const ImageInfo& requested);
@@ -143,6 +144,10 @@ private:
uint64_t address, uint64_t size) const; uint64_t address, uint64_t size) const;
void RetireImages(const std::vector<CachedImage*>& retire, void RetireImages(const std::vector<CachedImage*>& retire,
const CachedImage* native_image_source = nullptr); const CachedImage* native_image_source = nullptr);
void RetireDepthMetadataLocked(const std::vector<CachedImage*>& retire,
uint64_t preserve_address = 0);
void MaterializeImagesToGuestLocked(GraphicContext* ctx,
const std::vector<std::shared_ptr<CachedImage>>& images);
void SynchronizeColorImageToBufferLocked(CachedImage& cached, uint64_t write_address, void SynchronizeColorImageToBufferLocked(CachedImage& cached, uint64_t write_address,
uint64_t write_size); uint64_t write_size);
void SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint64_t write_address, void SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint64_t write_address,
+35 -4
View File
@@ -77,6 +77,10 @@ void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const
void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image, void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source, const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer) const { bool refresh, uint32_t base_layer) const {
if (info.samples != 1 || image == nullptr || image->samples != 1) {
EXIT("Tiler: multisampled depth upload is unsupported, samples=%u/%u\n", info.samples,
image != nullptr ? image->samples : 0);
}
if (refresh) { if (refresh) {
Transfer::WaitForGraphicsIdle(ctx); Transfer::WaitForGraphicsIdle(ctx);
} }
@@ -118,6 +122,10 @@ void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image, void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source, const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer) const { bool refresh, uint32_t base_layer) const {
if (info.samples != 1 || image == nullptr || image->samples != 1) {
EXIT("Tiler: multisampled stencil upload is unsupported, samples=%u/%u\n", info.samples,
image != nullptr ? image->samples : 0);
}
const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt); const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto stencil_pitch = TileGetTexturePitch( const auto stencil_pitch = TileGetTexturePitch(
stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth)); stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
@@ -150,7 +158,7 @@ void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info)
const bool standard64 = IsSupportedStandard64RenderTarget(info); const bool standard64 = IsSupportedStandard64RenderTarget(info);
if ((info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) && if ((info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
!standard64) || !standard64) ||
info.levels != 1) { info.levels != 1 || info.samples != 1) {
EXIT("Tiler: unsupported render-target tile, dst=%p src=%p " EXIT("Tiler: unsupported render-target tile, dst=%p src=%p "
"addr=0x%016" PRIx64 "+0x%016" PRIx64 "addr=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u levels=%u tile=%u bpe=%u\n", " extent=%ux%u pitch=%u levels=%u tile=%u bpe=%u\n",
@@ -203,9 +211,8 @@ void Tiler::TileImage(void* dst, const void* src, const ImageInfo& info) const {
} }
void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) const { void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) const {
if (info.stencil_address != 0 || info.stencil_size != 0 || if (info.samples != 1 || info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || !IsSupportedDepthReadbackFormat(info)) {
!IsSupportedDepthTargetFormat(info)) {
EXIT("Tiler: unsupported depth-target tile, dst=%p src=%p " EXIT("Tiler: unsupported depth-target tile, dst=%p src=%p "
"depth=0x%016" PRIx64 "+0x%016" PRIx64 " stencil=0x%016" PRIx64 "+0x%016" PRIx64 "depth=0x%016" PRIx64 "+0x%016" PRIx64 " stencil=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u tile=%u format=%d guest_format=%u bpe=%u\n", " extent=%ux%u pitch=%u tile=%u format=%d guest_format=%u bpe=%u\n",
@@ -214,6 +221,7 @@ void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) c
info.guest_format, info.bytes_per_element); info.guest_format, info.bytes_per_element);
} }
const auto slice_size = info.size / info.layers; const auto slice_size = info.size / info.layers;
std::memset(dst, 0, info.size);
for (uint32_t layer = 0; layer < info.layers; layer++) { for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer; auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer; auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
@@ -222,4 +230,27 @@ void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) c
} }
} }
void Tiler::TileStencil(void* dst, const void* src, const DepthTargetInfo& info) const {
const auto format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto pitch = TileGetTexturePitch(format, info.width, 1,
Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
if (info.samples != 1 || info.stencil_address == 0 || info.stencil_size == 0 ||
info.layers == 0 || info.stencil_size % info.layers != 0 ||
!IsSupportedDepthReadbackFormat(info)) {
EXIT("Tiler: unsupported stencil-target tile, dst=%p src=%p "
"stencil=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u layers=%u format=%d compressed=%d\n",
dst, src, info.stencil_address, info.stencil_size, info.width, info.height, pitch,
info.layers, static_cast<int>(info.format), info.stencil_htile_compressed);
}
const auto slice_size = info.stencil_size / info.layers;
std::memset(dst, 0, info.stencil_size);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
TileConvertLinearToTiledDepth(guest_slice, linear_slice, format, info.width, info.height,
pitch, slice_size);
}
}
} // namespace Libs::Graphics } // namespace Libs::Graphics
+1
View File
@@ -27,6 +27,7 @@ public:
void TileImage(void* dst, const void* src, const RenderTargetInfo& info) const; void TileImage(void* dst, const void* src, const RenderTargetInfo& info) const;
void TileImage(void* dst, const void* src, const ImageInfo& info) const; void TileImage(void* dst, const void* src, const ImageInfo& info) const;
void TileImage(void* dst, const void* src, const DepthTargetInfo& info) const; void TileImage(void* dst, const void* src, const DepthTargetInfo& info) const;
void TileStencil(void* dst, const void* src, const DepthTargetInfo& info) const;
}; };
} // namespace Libs::Graphics } // namespace Libs::Graphics
+1
View File
@@ -43,6 +43,7 @@ struct VulkanInstance {
bool memory_budget_ext_enabled = false; bool memory_budget_ext_enabled = false;
bool rt_extensions_enabled = false; bool rt_extensions_enabled = false;
bool subgroup_size_control_enabled = false; bool subgroup_size_control_enabled = false;
bool sample_rate_shading_enabled = false;
uint32_t subgroup_size = 0; uint32_t subgroup_size = 0;
uint32_t min_subgroup_size = 0; uint32_t min_subgroup_size = 0;
uint32_t max_subgroup_size = 0; uint32_t max_subgroup_size = 0;
@@ -556,7 +556,7 @@ static void VulkanInitSubgroupSizeControl(vk::PhysicalDevice physical_device, Gr
static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface, static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface,
const VulkanExtensions* r, const VulkanQueues& queues, const VulkanExtensions* r, const VulkanQueues& queues,
const std::vector<const char*>& device_extensions, const std::vector<const char*>& device_extensions,
const GraphicContext* ctx) { GraphicContext* ctx) {
EXIT_IF(physical_device == nullptr); EXIT_IF(physical_device == nullptr);
EXIT_IF(r == nullptr); EXIT_IF(r == nullptr);
EXIT_IF(surface == nullptr); EXIT_IF(surface == nullptr);
@@ -633,6 +633,8 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::Sur
device_features.shaderImageGatherExtended = VK_TRUE; device_features.shaderImageGatherExtended = VK_TRUE;
device_features.independentBlend = VK_TRUE; device_features.independentBlend = VK_TRUE;
device_features.tessellationShader = VK_TRUE; device_features.tessellationShader = VK_TRUE;
device_features.sampleRateShading = supported_features2.features.sampleRateShading;
ctx->sample_rate_shading_enabled = device_features.sampleRateShading == VK_TRUE;
device_features.vertexPipelineStoresAndAtomics = device_features.vertexPipelineStoresAndAtomics =
supported_features2.features.vertexPipelineStoresAndAtomics; supported_features2.features.vertexPipelineStoresAndAtomics;
+5 -2
View File
@@ -819,6 +819,7 @@ static void ShaderGetStaticInputInfoPS(
ps_info->ps_pos_z = (active_inputs & 0x00000400u) != 0; ps_info->ps_pos_z = (active_inputs & 0x00000400u) != 0;
ps_info->ps_pos_w = (active_inputs & 0x00000800u) != 0; ps_info->ps_pos_w = (active_inputs & 0x00000800u) != 0;
ps_info->ps_front_face = (active_inputs & 0x00001000u) != 0; ps_info->ps_front_face = (active_inputs & 0x00001000u) != 0;
ps_info->ps_sample_shading = (active_inputs & 0x00000011u) != 0;
ps_info->ps_no_perspective = (sh->ps_input_ena & sh->ps_input_addr & 0x00000020u) != 0; ps_info->ps_no_perspective = (sh->ps_input_ena & sh->ps_input_addr & 0x00000020u) != 0;
ps_info->ps_pixel_kill_enable = sh->db_shader_control.shader_kill_enable; ps_info->ps_pixel_kill_enable = sh->db_shader_control.shader_kill_enable;
ps_info->ps_depth_export_enable = sh->db_shader_control.shader_z_export_enable; ps_info->ps_depth_export_enable = sh->db_shader_control.shader_z_export_enable;
@@ -1260,6 +1261,7 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo* info) {
"\t ps_pos_z = %s\n" "\t ps_pos_z = %s\n"
"\t ps_pos_w = %s\n" "\t ps_pos_w = %s\n"
"\t ps_front_face = %s\n" "\t ps_front_face = %s\n"
"\t ps_sample_shading = %s\n"
"\t ps_no_perspective = %s\n" "\t ps_no_perspective = %s\n"
"\t ps_pixel_kill_enable = %s\n" "\t ps_pixel_kill_enable = %s\n"
"\t ps_early_z = %s\n" "\t ps_early_z = %s\n"
@@ -1267,8 +1269,9 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo* info) {
info->input_num, info->ps_system_input_base, info->ps_pos_x ? "true" : "false", info->input_num, info->ps_system_input_base, info->ps_pos_x ? "true" : "false",
info->ps_pos_y ? "true" : "false", info->ps_pos_z ? "true" : "false", info->ps_pos_y ? "true" : "false", info->ps_pos_z ? "true" : "false",
info->ps_pos_w ? "true" : "false", info->ps_front_face ? "true" : "false", info->ps_pos_w ? "true" : "false", info->ps_front_face ? "true" : "false",
info->ps_no_perspective ? "true" : "false", info->ps_pixel_kill_enable ? "true" : "false", info->ps_sample_shading ? "true" : "false", info->ps_no_perspective ? "true" : "false",
info->ps_early_z ? "true" : "false", info->ps_execute_on_noop ? "true" : "false"); info->ps_pixel_kill_enable ? "true" : "false", info->ps_early_z ? "true" : "false",
info->ps_execute_on_noop ? "true" : "false");
for (uint32_t i = 0; i < info->input_num; i++) { for (uint32_t i = 0; i < info->input_num; i++) {
LOGF("\t interpolator_settings[%u] = %u\n", i, info->interpolator_settings[i]); LOGF("\t interpolator_settings[%u] = %u\n", i, info->interpolator_settings[i]);
+1
View File
@@ -113,6 +113,7 @@ struct ShaderPixelInputInfo {
bool ps_pixel_kill_enable = false; bool ps_pixel_kill_enable = false;
bool ps_depth_export_enable = false; bool ps_depth_export_enable = false;
bool ps_sample_mask_export_enable = false; bool ps_sample_mask_export_enable = false;
bool ps_sample_shading = false;
bool ps_early_z = false; bool ps_early_z = false;
bool ps_execute_on_noop = false; bool ps_execute_on_noop = false;
ShaderStageRuntime stage; ShaderStageRuntime stage;
+443 -114
View File
@@ -9244,6 +9244,45 @@ 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),
"RGBA did not select the identity view"); "RGBA did not select the identity view");
ShaderRecompiler::IR::ImageResource cube_resource{};
cube_resource.kind = ShaderRecompiler::IR::ResourceKind::Image;
cube_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
cube_resource.read = true;
const auto cube_view =
ResolveTargetTextureView(cube_resource, Prospero::ImageType::kCube, 0, 6);
Require("SampledColorViews", "PPSA17337 cubemap render target",
cube_view.type == vk::ImageViewType::e2DArray &&
cube_view.base_layer == 0 && cube_view.layer_count == 6,
"captured six-face cubemap did not resolve to a 2D-array view");
const auto cube_array_view = ResolveTargetTextureView(
cube_resource, Prospero::ImageType::kCube, 6, 18);
Require("SampledColorViews", "cubemap array subview",
cube_array_view.type == vk::ImageViewType::e2DArray &&
cube_array_view.base_layer == 6 &&
cube_array_view.layer_count == 12,
"nonzero-base multi-cube view did not preserve whole face groups");
auto non_array_cube_resource = cube_resource;
non_array_cube_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2D;
Require("SampledColorViews", "cubemap hard guards",
ResolveTargetTextureView(non_array_cube_resource,
Prospero::ImageType::kCube, 0, 6)
.type ==
static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM) &&
ResolveTargetTextureView(cube_resource,
Prospero::ImageType::kCube, 0, 7)
.type ==
static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM) &&
ResolveTargetTextureView(cube_resource,
Prospero::ImageType::kCube, 1, 6)
.type ==
static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM) &&
ResolveTargetTextureView(cube_resource,
Prospero::ImageType::kCube, 6, 6)
.type ==
static_cast<vk::ImageViewType>(VK_IMAGE_VIEW_TYPE_MAX_ENUM),
"non-array or partial cubemap views were accepted");
uint32_t valid_swizzles = 0; uint32_t valid_swizzles = 0;
for (uint32_t swizzle = 0; swizzle <= 0xfffu; swizzle++) { for (uint32_t swizzle = 0; swizzle <= 0xfffu; swizzle++) {
bool expected = true; bool expected = true;
@@ -11133,23 +11172,41 @@ void CheckStorageTextureDepthAlias() {
depth.stencil_address = storage.address; depth.stencil_address = storage.address;
depth.stencil_size = storage.size; depth.stencil_size = storage.size;
Require("StorageTextureDepthAlias", "inactive stencil", Require("StorageTextureDepthAlias", "inactive stencil",
ClassifyStorageDepthOverlap(storage, depth) == ClassifyStorageDepthOverlap(storage, true, false, false, true, true,
DepthOverlap::RetireStorage, depth) == DepthOverlap::RetireStorage,
"inactive stencil storage ownership was not retired"); "inactive stencil storage ownership was not retired");
depth.stencil_access = true; depth.stencil_access = true;
Require("StorageTextureDepthAlias", "active stencil", Require("StorageTextureDepthAlias", "active stencil",
ClassifyStorageDepthOverlap(storage, depth) == ClassifyStorageDepthOverlap(storage, true, false, false, true, true,
DepthOverlap::Unsupported, depth) == DepthOverlap::Unsupported,
"active stencil storage contents were discarded"); "active stencil storage contents were discarded");
depth.stencil_access = false; depth.stencil_access = false;
storage.address = depth.address; storage.address = depth.address;
depth.depth_access = true;
Require("StorageTextureDepthAlias", "depth aspect", Require("StorageTextureDepthAlias", "depth aspect",
ClassifyStorageDepthOverlap(storage, depth) == ClassifyStorageDepthOverlap(storage, true, false, false, true, true,
DepthOverlap::Unsupported, depth) == DepthOverlap::Unsupported,
"depth-aspect storage contents were discarded"); "depth-aspect storage contents were discarded");
Require("StorageTextureDepthAlias", "clean depth transition",
ClassifyStorageDepthOverlap(storage, false, false, false, false, true,
depth) == DepthOverlap::RetireStorage,
"guest-current storage image was not retired for a depth target");
Require(
"StorageTextureDepthAlias", "depth ownership guards",
ClassifyStorageDepthOverlap(storage, false, true, false, false, true,
depth) == DepthOverlap::Unsupported &&
ClassifyStorageDepthOverlap(storage, false, false, true, false, true,
depth) == DepthOverlap::Unsupported &&
ClassifyStorageDepthOverlap(storage, false, false, false, true, true,
depth) == DepthOverlap::Unsupported &&
ClassifyStorageDepthOverlap(storage, false, false, false, false,
false,
depth) == DepthOverlap::Unsupported,
"incoherent storage-to-depth transition was admitted");
storage.address = 0x2000000000ull; storage.address = 0x2000000000ull;
Require("StorageTextureDepthAlias", "disjoint", Require("StorageTextureDepthAlias", "disjoint",
ClassifyStorageDepthOverlap(storage, depth) == DepthOverlap::None, ClassifyStorageDepthOverlap(storage, true, true, true, true, false,
depth) == DepthOverlap::None,
"disjoint storage image was classified as an alias"); "disjoint storage image was classified as an alias");
std::printf("[host] %-32s ok\n", "StorageTextureDepthAlias"); std::printf("[host] %-32s ok\n", "StorageTextureDepthAlias");
} }
@@ -11238,11 +11295,11 @@ void CheckStorageTextureAccessPermissions() {
TextureCache texture_cache(page_manager, buffer_cache, resource_mutex); TextureCache texture_cache(page_manager, buffer_cache, resource_mutex);
buffer_cache.SetTextureCache(texture_cache); buffer_cache.SetTextureCache(texture_cache);
page_manager.OnGpuMap(address, allocation_size); page_manager.OnGpuMap(address, allocation_size);
texture_cache.RegisterMeta(address, 0x8000);
auto *ctx = reinterpret_cast<GraphicContext *>(1); auto *ctx = reinterpret_cast<GraphicContext *>(1);
texture_cache.RegisterMeta(ctx, address, 0x8000);
if (std::strcmp(kind, "metadata-size") == 0) { if (std::strcmp(kind, "metadata-size") == 0) {
texture_cache.RegisterMeta(address, 0x10000); texture_cache.RegisterMeta(ctx, address, 0x10000);
} else if (std::strcmp(kind, "texture") == 0) { } else if (std::strcmp(kind, "texture") == 0) {
ImageInfo info{}; ImageInfo info{};
info.address = address; info.address = address;
@@ -11377,8 +11434,8 @@ void CheckOverlappingMetadataViews() {
buffer_cache.SetTextureCache(texture_cache); buffer_cache.SetTextureCache(texture_cache);
page_manager.OnGpuMap(base, allocation_size); page_manager.OnGpuMap(base, allocation_size);
texture_cache.RegisterMeta(base, metadata_size); texture_cache.RegisterMeta(nullptr, base, metadata_size);
texture_cache.RegisterMeta(second, metadata_size); texture_cache.RegisterMeta(nullptr, second, metadata_size);
Require("OverlappingMetadataViews", "registration", Require("OverlappingMetadataViews", "registration",
texture_cache.IsMeta(base) && texture_cache.IsMeta(second) && texture_cache.IsMeta(base) && texture_cache.IsMeta(second) &&
texture_cache.IsMetaRange(base, metadata_size) && texture_cache.IsMetaRange(base, metadata_size) &&
@@ -11432,7 +11489,7 @@ void CheckQueryRegionAggregation() {
!empty.metadata_bytes && !empty.gpu_metadata_bytes, !empty.metadata_bytes && !empty.gpu_metadata_bytes,
"empty region reported cached ownership"); "empty region reported cached ownership");
texture_cache.RegisterMeta(base, metadata_size); texture_cache.RegisterMeta(nullptr, base, metadata_size);
const auto bytes = texture_cache.QueryRegion(base + 0x20, 0x40); const auto bytes = texture_cache.QueryRegion(base + 0x20, 0x40);
const auto page_only = texture_cache.QueryRegion(base + 0x400, 0x40); const auto page_only = texture_cache.QueryRegion(base + 0x400, 0x40);
const auto disjoint = texture_cache.QueryRegion(base + 0x1000, 0x40); const auto disjoint = texture_cache.QueryRegion(base + 0x1000, 0x40);
@@ -11485,7 +11542,7 @@ void CheckGpuMetadataReuse() {
buffer_cache.SetTextureCache(texture_cache); buffer_cache.SetTextureCache(texture_cache);
page_manager.OnGpuMap(base, allocation_size); page_manager.OnGpuMap(base, allocation_size);
texture_cache.RegisterMeta(base, metadata_size, layers); texture_cache.RegisterMeta(nullptr, base, metadata_size, layers);
Require("GpuMetadataReuse", "clear", texture_cache.ClearMeta(base), Require("GpuMetadataReuse", "clear", texture_cache.ClearMeta(base),
"metadata clear setup failed"); "metadata clear setup failed");
const bool full_image_transition = const bool full_image_transition =
@@ -11495,7 +11552,7 @@ void CheckGpuMetadataReuse() {
!texture_cache.QueryRegion(base, allocation_size).metadata_bytes, !texture_cache.QueryRegion(base, allocation_size).metadata_bytes,
"metadata-only overwrite retained identity or claimed an image " "metadata-only overwrite retained identity or claimed an image "
"transition"); "transition");
texture_cache.RegisterMeta(base, metadata_size, layers); texture_cache.RegisterMeta(nullptr, base, metadata_size, layers);
Require("GpuMetadataReuse", "re-register", Require("GpuMetadataReuse", "re-register",
texture_cache.IsMetaRange(base, metadata_size) && texture_cache.IsMetaRange(base, metadata_size) &&
texture_cache.ClearMeta(base), texture_cache.ClearMeta(base),
@@ -11730,6 +11787,8 @@ void CheckBufferImageWrites() {
old_depth.address = 0x4a5c90000ull; old_depth.address = 0x4a5c90000ull;
old_depth.size = 0x10000; old_depth.size = 0x10000;
old_depth.width = old_depth.height = 32; old_depth.width = old_depth.height = 32;
old_depth.pitch = 32;
old_depth.bytes_per_element = 4;
old_depth.layers = 1; old_depth.layers = 1;
DepthTargetInfo cleared_depth = old_depth; DepthTargetInfo cleared_depth = old_depth;
cleared_depth.width = cleared_depth.height = 64; cleared_depth.width = cleared_depth.height = 64;
@@ -11745,21 +11804,137 @@ void CheckBufferImageWrites() {
ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) == ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) ==
DepthOverlap::Unsupported, DepthOverlap::Unsupported,
"loaded depth reinterpretation discarded live native contents"); "loaded depth reinterpretation discarded live native contents");
RenderTargetInfo old_color{};
old_color.address = 0x4a5cb0000ull;
old_color.size = 0x10000;
old_color.width = old_color.height = old_color.pitch = 128;
old_color.bytes_per_element = 4;
old_color.format = vk::Format::eR8G8B8A8Unorm;
auto reinterpreted_color = old_color;
reinterpreted_color.format = vk::Format::eR32Uint;
Require("BufferImageWrite", "clean color format recreation",
ClassifyRenderTargetOverlap(old_color, false, false, false, true,
true, reinterpreted_color) ==
RenderTargetOverlap::RetireTarget &&
ClassifyRenderTargetOverlap(old_color, true, false, true, true,
true, reinterpreted_color) ==
RenderTargetOverlap::Unsupported,
"clean equal-base color allocation did not preserve GPU-owned format "
"failures");
auto pool_depth = old_depth;
pool_depth.stencil_address = old_depth.address + 0x20000;
pool_depth.stencil_size = old_depth.size;
auto shifted_depth = pool_depth;
shifted_depth.address = old_depth.address - 0x20000;
shifted_depth.stencil_address = old_depth.address;
Require("BufferImageWrite", "shifted depth-plane recreation",
ClassifyDepthTargetOverlap(pool_depth, true, false, true,
shifted_depth) ==
DepthOverlap::RecreateTarget &&
ClassifyDepthTargetOverlap(pool_depth, true, true, true,
shifted_depth) ==
DepthOverlap::Unsupported,
"exact stencil/depth pool reuse did not require clean single-sample "
"recreation");
shifted_depth.samples = 8;
Require(
"BufferImageWrite", "multisampled depth-plane recreation",
ClassifyDepthTargetOverlap(pool_depth, true, false, true,
shifted_depth) == DepthOverlap::Unsupported,
"multisampled depth-plane reuse bypassed the unsupported readback guard");
RenderTargetInfo pool_color{};
pool_color.address = shifted_depth.stencil_address;
pool_color.size = shifted_depth.stencil_size;
shifted_depth.samples = 1;
Require(
"BufferImageWrite", "color/depth pool recreation",
CanRecreateRenderTargetForDepth(pool_color, false, false, false, true,
true, shifted_depth) &&
!CanRecreateRenderTargetForDepth(pool_color, false, true, false, true,
true, shifted_depth),
"exact color-plane reuse did not require clean single-sample recreation");
pool_color.size *= 2;
Require(
"BufferImageWrite", "larger color/depth pool recreation",
CanRecreateRenderTargetForDepth(pool_color, false, false, false, true,
true, shifted_depth),
"equal-base page-isolated color allocation was not recreated for depth");
auto contained_depth = shifted_depth;
contained_depth.address = pool_color.address + TRACKER_PAGE_SIZE;
contained_depth.size = TRACKER_PAGE_SIZE;
contained_depth.stencil_address = 0;
contained_depth.stencil_size = 0;
Require(
"BufferImageWrite", "contained color/depth pool recreation",
CanRecreateRenderTargetForDepth(pool_color, false, false, false, true,
true, contained_depth) &&
!CanRecreateRenderTargetForDepth(pool_color, false, false, false,
true, false, contained_depth) &&
!CanRecreateRenderTargetForDepth(pool_color, true, false, false, true,
true, contained_depth),
"contained depth allocation bypassed source or tracker ownership guards");
pool_color.samples = 8;
Require(
"BufferImageWrite", "multisampled color/depth recreation",
!CanRecreateRenderTargetForDepth(pool_color, false, false, false, true,
true, shifted_depth),
"multisampled color-plane reuse bypassed the unsupported readback guard");
auto multisample_depth = old_depth;
multisample_depth.samples = 8;
multisample_depth.depth_access = true;
multisample_depth.stencil_access = true;
Require("BufferImageWrite", "multisampled depth refresh state",
!RequiresMultisampleDepthRefresh(multisample_depth, false, false,
false) &&
RequiresMultisampleDepthRefresh(multisample_depth, false, true,
false) &&
RequiresMultisampleDepthRefresh(multisample_depth, false, false,
true) &&
RequiresMultisampleDepthRefresh(multisample_depth, true, false,
false),
"multisampled refresh used consumed source history instead of "
"current plane ownership");
RenderTargetInfo color_alias{}; RenderTargetInfo color_alias{};
color_alias.address = old_depth.address; color_alias.address = old_depth.address;
color_alias.size = old_depth.size; color_alias.size = old_depth.size;
color_alias.width = color_alias.height = 32;
color_alias.pitch = 32;
color_alias.bytes_per_element = 4;
Require("BufferImageWrite", "guest-backed depth to color",
CanRecreateDepthForRenderTarget(old_depth, false, false, false, true,
true, color_alias) &&
CanRecreateDepthForRenderTarget(old_depth, true, false, true,
true, false, color_alias) &&
!CanRecreateDepthForRenderTarget(old_depth, true, true, true,
true, true, color_alias),
"depth-to-color recreation did not require coherent guest ownership");
Require("BufferImageWrite", "guest-current depth to color",
!CanRecreateDepthForRenderTarget(old_depth, false, false, false, true,
false, color_alias),
"stale guest backing authorized depth-to-color recreation");
auto mismatched_color = color_alias;
mismatched_color.address += 0x1000;
Require("BufferImageWrite", "mismatched depth to color",
!CanRecreateDepthForRenderTarget(old_depth, false, false, false, true,
true, mismatched_color),
"mismatched depth shape was discarded as a color allocation");
color_alias.address = pool_depth.stencil_address;
Require( Require(
"BufferImageWrite", "buffer-owned depth to color", "BufferImageWrite", "stencil-plane depth to color",
CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, CanRecreateDepthForRenderTarget(pool_depth, false, false, false, true,
true, color_alias) && true, color_alias),
!CanRetireBufferOwnedDepthForRenderTarget(old_depth, true, false, "exact stencil-plane reuse was not treated as whole-image recreation");
true, true, color_alias), auto containing_color = color_alias;
"depth-to-color recreation did not require coherent buffer ownership"); containing_color.address = pool_depth.stencil_address - 0x40000;
Require( containing_color.size = 0x80000;
"BufferImageWrite", "page-neighbor depth to color", Require("BufferImageWrite", "contained depth allocation to color",
!CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, CanRecreateDepthForRenderTarget(pool_depth, false, false, false, true,
false, color_alias), true, containing_color) &&
"same-page byte-disjoint buffer authorized depth-to-color recreation"); !CanRecreateDepthForRenderTarget(pool_depth, false, false, false,
true, false, containing_color) &&
!CanRecreateDepthForRenderTarget(pool_depth, false, false, true,
true, true, containing_color),
"contained guest-current depth allocation bypassed ownership guards");
TileSizeAlign storage{}; TileSizeAlign storage{};
Require("BufferImageWrite", "target layout", Require("BufferImageWrite", "target layout",
@@ -12269,33 +12444,52 @@ void CheckImageOverlapResolution() {
storage_target.tile_mode = storage.tile; storage_target.tile_mode = storage.tile;
Require("ImageOverlapResolution", "storage render-target native transition", Require("ImageOverlapResolution", "storage render-target native transition",
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, true, false, false, true, storage, storage_target.format, true, false, false, true, true,
storage_target) == RenderTargetOverlap::PreserveStorage, storage_target) == RenderTargetOverlap::PreserveStorage,
"exact GPU-owned RGBA16F storage image was not preserved for a " "exact GPU-owned RGBA16F storage image was not preserved for a "
"render target"); "render target");
auto partial_storage_target = storage_target;
partial_storage_target.address += 0x10000;
partial_storage_target.size = 0x10000;
Require("ImageOverlapResolution", "clean storage render-target transition",
ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, false, false, false, false, true,
partial_storage_target) == RenderTargetOverlap::RetireStorage,
"clean storage allocation was not retired for an overlapping render "
"target");
auto mismatched_storage_target = storage_target; auto mismatched_storage_target = storage_target;
mismatched_storage_target.width--; mismatched_storage_target.width--;
Require("ImageOverlapResolution", "storage render-target guards", Require(
"ImageOverlapResolution", "storage render-target guards",
ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, true, true, false, true, true,
storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, false, false, false, true, storage, storage_target.format, true, false, true, true, true,
storage_target) == RenderTargetOverlap::Unsupported && storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, true, true, false, true, storage, storage_target.format, true, false, false, true, false,
storage_target) == RenderTargetOverlap::Unsupported && storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, true, false, true, true, storage, vk::Format::eR32G32B32A32Sfloat, true, false, false,
storage_target) == RenderTargetOverlap::Unsupported && true, true, storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, true, false, false, false, storage, storage_target.format, true, false, false, true, true,
storage_target) == RenderTargetOverlap::Unsupported && mismatched_storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap( ClassifyStorageRenderTargetOverlap(
storage, vk::Format::eR32G32B32A32Sfloat, true, false, false, storage, storage_target.format, false, false, false, true, true,
true, storage_target) == RenderTargetOverlap::Unsupported && storage_target) == RenderTargetOverlap::Unsupported &&
ClassifyStorageRenderTargetOverlap(storage, storage_target.format, ClassifyStorageRenderTargetOverlap(
true, false, false, true, storage, storage_target.format, true, false, false, false, true,
mismatched_storage_target) == storage_target) == RenderTargetOverlap::Unsupported,
RenderTargetOverlap::Unsupported, "unsupported storage-to-render-target transition was admitted");
"unsupported storage-to-render-target transition was admitted"); partial_storage_target.address = storage.address + storage.size;
Require("ImageOverlapResolution", "storage render-target adjacency",
ClassifyStorageRenderTargetOverlap(
storage, storage_target.format, false, false, false, false, true,
partial_storage_target) == RenderTargetOverlap::None,
"byte-disjoint storage and render-target allocations were treated as "
"aliases");
Require("ImageOverlapResolution", "render target context", Require("ImageOverlapResolution", "render target context",
ClassifyRenderTargetOverlap(sampled, false, false, target) == ClassifyRenderTargetOverlap(sampled, false, false, target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
@@ -12362,40 +12556,51 @@ void CheckImageOverlapResolution() {
replacement_target.height = 128; replacement_target.height = 128;
replacement_target.bytes_per_element = 8; replacement_target.bytes_per_element = 8;
Require("ImageOverlapResolution", "render target allocation-pool replacement", Require("ImageOverlapResolution", "render target allocation-pool replacement",
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true,
replacement_target) == true, replacement_target) ==
RenderTargetOverlap::RetireTarget, RenderTargetOverlap::RetireTarget,
"clean same-base render-target pool allocation was not retired"); "clean same-base render-target pool allocation was not retired");
Require("ImageOverlapResolution", "render target allocation-pool GPU owner", Require("ImageOverlapResolution", "render target allocation-pool GPU owner",
ClassifyRenderTargetOverlap(old_target, true, false, true, ClassifyRenderTargetOverlap(old_target, true, false, true, true, true,
replacement_target) == replacement_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
"GPU-owned render-target pool allocation was retired"); "GPU-owned render-target pool allocation was retired");
Require("ImageOverlapResolution", Require("ImageOverlapResolution",
"render target allocation-pool buffer owner", "render target allocation-pool buffer owner",
ClassifyRenderTargetOverlap(old_target, false, true, true, ClassifyRenderTargetOverlap(old_target, false, true, false, true,
replacement_target) == true, replacement_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
"buffer-dirty render-target pool allocation was retired"); "buffer-dirty render-target pool allocation was retired");
Require("ImageOverlapResolution", "render target allocation-pool context", Require("ImageOverlapResolution", "render target allocation-pool context",
ClassifyRenderTargetOverlap(old_target, false, false, false, ClassifyRenderTargetOverlap(old_target, false, false, false, false,
replacement_target) == true, replacement_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
"cross-context render-target pool allocation was retired"); "cross-context render-target pool allocation was retired");
auto offset_target = replacement_target; auto offset_target = replacement_target;
offset_target.address += TRACKER_PAGE_SIZE; offset_target.address += TRACKER_PAGE_SIZE;
Require("ImageOverlapResolution", "render target allocation-pool offset", Require("ImageOverlapResolution", "render target allocation-pool offset",
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true,
offset_target) == true, offset_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::RetireTarget,
"offset render-target overlap was treated as allocation-pool " "clean offset render-target allocation was not retired");
"replacement"); auto contained_target = old_target;
contained_target.address += TRACKER_PAGE_SIZE;
contained_target.size = TRACKER_PAGE_SIZE;
Require("ImageOverlapResolution",
"render target contained allocation-pool reuse",
ClassifyRenderTargetOverlap(old_target, false, false, false, true,
true, contained_target) ==
RenderTargetOverlap::RetireTarget &&
ClassifyRenderTargetOverlap(old_target, false, false, false, true,
false, contained_target) ==
RenderTargetOverlap::Unsupported,
"contained render-target allocation bypassed guest ownership guards");
auto partial_page_target = replacement_target; auto partial_page_target = replacement_target;
partial_page_target.address++; partial_page_target.address++;
Require("ImageOverlapResolution", Require("ImageOverlapResolution",
"render target allocation-pool partial page", "render target allocation-pool partial page",
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true,
partial_page_target) == true, partial_page_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
"partially shared tracker page was treated as allocation-pool " "partially shared tracker page was treated as allocation-pool "
"replacement"); "replacement");
@@ -12406,8 +12611,8 @@ void CheckImageOverlapResolution() {
"exact RGBA8 UNORM-to-sRGB target was not recognized as a compatible " "exact RGBA8 UNORM-to-sRGB target was not recognized as a compatible "
"view"); "view");
Require("ImageOverlapResolution", "render target sRGB no retirement", Require("ImageOverlapResolution", "render target sRGB no retirement",
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true,
same_shape_target) == true, same_shape_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::Unsupported,
"compatible render-target view fell through to target retirement"); "compatible render-target view fell through to target retirement");
auto incompatible_same_shape_target = same_shape_target; auto incompatible_same_shape_target = same_shape_target;
@@ -12415,10 +12620,11 @@ void CheckImageOverlapResolution() {
Require("ImageOverlapResolution", "render target incompatible same shape", Require("ImageOverlapResolution", "render target incompatible same shape",
!IsCompatibleRenderTargetView(old_target, !IsCompatibleRenderTargetView(old_target,
incompatible_same_shape_target) && incompatible_same_shape_target) &&
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true,
true,
incompatible_same_shape_target) == incompatible_same_shape_target) ==
RenderTargetOverlap::Unsupported, RenderTargetOverlap::RetireTarget,
"incompatible same-storage render-target format was admitted"); "clean incompatible render-target format was not recreated");
RenderTargetInfo ppsa02604_unorm_target{}; RenderTargetInfo ppsa02604_unorm_target{};
ppsa02604_unorm_target.address = 0x79c50000ull; ppsa02604_unorm_target.address = 0x79c50000ull;
@@ -12441,29 +12647,107 @@ void CheckImageOverlapResolution() {
adjacent_target.address = old_target.address + old_target.size; adjacent_target.address = old_target.address + old_target.size;
Require( Require(
"ImageOverlapResolution", "render target allocation-pool adjacent", "ImageOverlapResolution", "render target allocation-pool adjacent",
ClassifyRenderTargetOverlap(old_target, false, false, true, ClassifyRenderTargetOverlap(old_target, false, false, false, true, true,
adjacent_target) == RenderTargetOverlap::None, adjacent_target) == RenderTargetOverlap::None,
"adjacent render targets were treated as allocation-pool replacement"); "adjacent render targets were treated as allocation-pool replacement");
Require("ImageOverlapResolution", "metadata retains sampled image", Require("ImageOverlapResolution", "metadata retains sampled image",
ClassifyMetaImageOverlap(true, false, false, false) == ClassifyMetaImageOverlap(true, false, false, false, false, true) ==
MetaImageOverlap::RetainSampled, MetaImageOverlap::RetainSampled,
"CPU-current sampled metadata alias was rejected"); "CPU-current sampled metadata alias was rejected");
Require("ImageOverlapResolution", "metadata retires render target", Require("ImageOverlapResolution", "metadata retires writable image",
ClassifyMetaImageOverlap(false, true, false, false) == ClassifyMetaImageOverlap(false, true, false, false, false, true) ==
MetaImageOverlap::RetireTarget, MetaImageOverlap::RetireImage,
"CPU-current render target was not retired for metadata reuse"); "guest-current writable image was not retired for metadata reuse");
Require("ImageOverlapResolution", "metadata rejects GPU target", Require("ImageOverlapResolution", "metadata rejects GPU target",
ClassifyMetaImageOverlap(false, true, true, false) == ClassifyMetaImageOverlap(false, true, true, false, false, true) ==
MetaImageOverlap::Unsupported && MetaImageOverlap::Unsupported &&
ClassifyMetaImageOverlap(false, true, false, true) == ClassifyMetaImageOverlap(false, true, false, true, false, true) ==
MetaImageOverlap::Unsupported &&
ClassifyMetaImageOverlap(false, true, false, false, true, true) ==
MetaImageOverlap::Unsupported, MetaImageOverlap::Unsupported,
"unordered or buffer-dirty render target was admitted for metadata " "unordered or dirty writable image was admitted for metadata "
"reuse"); "reuse");
Require("ImageOverlapResolution", "metadata rejects unsupported image", Require("ImageOverlapResolution", "metadata rejects unsupported image",
ClassifyMetaImageOverlap(false, false, false, false) == ClassifyMetaImageOverlap(false, false, false, false, false, true) ==
MetaImageOverlap::Unsupported, MetaImageOverlap::Unsupported,
"unsupported cached image kind was admitted for metadata reuse"); "unsupported cached image kind was admitted for metadata reuse");
Require("ImageOverlapResolution", "metadata rejects cross-context target",
ClassifyMetaImageOverlap(false, true, false, false, false, false) ==
MetaImageOverlap::Unsupported,
"cross-context writable image was retired for metadata reuse");
Require(
"ImageOverlapResolution", "guest-current depth metadata pool reuse",
CanRetireGuestCurrentDepthForMetadataReuse(false, false, false, false,
false, 0, true),
"guest-current depth owner was not retired when its HTile allocation was "
"reused");
Require(
"ImageOverlapResolution", "depth metadata pool ownership guards",
!CanRetireGuestCurrentDepthForMetadataReuse(true, false, true, false,
false, 0, true) &&
!CanRetireGuestCurrentDepthForMetadataReuse(false, true, false, false,
false, 0, true) &&
!CanRetireGuestCurrentDepthForMetadataReuse(false, false, false, true,
true, 1, true) &&
!CanRetireGuestCurrentDepthForMetadataReuse(false, false, false,
false, false, 0, false),
"GPU-owned, buffer-owned, cleared, or cross-context HTile metadata was "
"admitted for pool reuse");
ImageInfo pooled_sampled{};
pooled_sampled.address = 0x71141000;
pooled_sampled.size = 0x40000;
DepthTargetInfo pooled_depth{};
pooled_depth.address = 0x71150000;
pooled_depth.size = 0x10000;
pooled_depth.stencil_address = 0x71170000;
pooled_depth.stencil_size = 0x10000;
Require("ImageOverlapResolution", "guest-current sampled/depth pool alias",
CanRetireGuestCurrentDepthForSampled(
pooled_sampled, pooled_depth, false, false, false, false, true),
"guest-current contained depth target was not retired for a sampled "
"allocation");
Require(
"ImageOverlapResolution", "sampled/depth pool ownership guards",
!CanRetireGuestCurrentDepthForSampled(pooled_sampled, pooled_depth, true,
false, true, true, true) &&
!CanRetireGuestCurrentDepthForSampled(
pooled_sampled, pooled_depth, false, true, false, false, true) &&
!CanRetireGuestCurrentDepthForSampled(
pooled_sampled, pooled_depth, false, false, false, false, false),
"GPU-owned, buffer-owned, or cross-context sampled/depth alias was "
"admitted");
ImageInfo offset_sampled{};
offset_sampled.address = 0x6c6cd000;
offset_sampled.size = 0x10000;
DepthTargetInfo offset_depth{};
offset_depth.address = 0x6c6d0000;
offset_depth.size = 0x10000;
Require("ImageOverlapResolution", "guest-current sampled to depth pool alias",
CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth,
false, false, false, false, true,
true),
"offset guest-current sampled allocation was not retired for depth "
"reuse");
Require(
"ImageOverlapResolution", "sampled to depth pool ownership guards",
!CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth, true,
false, false, true, true, true) &&
!CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth,
false, true, false, false, true,
true) &&
!CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth,
false, false, true, false, true,
true) &&
!CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth,
false, false, false, false,
false, true) &&
!CanRetireGuestCurrentSampledForDepth(offset_sampled, offset_depth,
false, false, false, false,
true, false),
"dirty, cross-context, or stale sampled allocation was admitted for "
"depth reuse");
const std::array isolated_retirement{ const std::array isolated_retirement{
ImageRetirementRange{sampled.address, TRACKER_PAGE_SIZE, true}, ImageRetirementRange{sampled.address, TRACKER_PAGE_SIZE, true},
@@ -12717,32 +13001,44 @@ void CheckImageOverlapResolution() {
std::printf("[host] %-32s ok\n", "ImageOverlapResolution"); std::printf("[host] %-32s ok\n", "ImageOverlapResolution");
} }
void CheckMsaaCompatibility() { void CheckNativeMsaaState() {
Require("MsaaCompatibility", "single sample", Require("NativeMsaaState", "sample encoding",
!depth_msaa_single_sample_compatible(0), render_sample_count(0) == 1 && render_sample_count(1) == 2 &&
"native single-sample state was classified as compatibility"); render_sample_count(2) == 4 && render_sample_count(3) == 8 &&
Require("MsaaCompatibility", "two fragments", render_sample_count(4) == 0,
depth_msaa_single_sample_compatible(1), "PS5 sample encodings were not mapped exactly");
"two-fragment compatibility state was rejected"); Require("NativeMsaaState", "Vulkan sample mapping",
Require("MsaaCompatibility", "four fragments", vulkan_sample_count(1) == vk::SampleCountFlagBits::e1 &&
depth_msaa_single_sample_compatible(2), vulkan_sample_count(2) == vk::SampleCountFlagBits::e2 &&
"existing four-fragment compatibility state regressed"); vulkan_sample_count(4) == vk::SampleCountFlagBits::e4 &&
Require("MsaaCompatibility", "eight fragments", vulkan_sample_count(8) == vk::SampleCountFlagBits::e8 &&
!depth_msaa_single_sample_compatible(3), vulkan_sample_count(3) == vk::SampleCountFlagBits{},
"unsupported eight-fragment state was silently admitted"); "native sample counts were not mapped exactly to Vulkan");
Require("MsaaCompatibility", "color two fragments",
color_msaa_single_sample_compatible(1, 1), TileSizeAlign color{};
"matching two-sample color compatibility state was rejected"); const auto color_pitch = TileGetRenderTargetPitch(1920, 8, 3);
Require("MsaaCompatibility", "color four fragments", Require("NativeMsaaState", "8x color footprint",
color_msaa_single_sample_compatible(2, 2), color_pitch == 1920 &&
"existing matching four-sample color compatibility state regressed"); TileGetRenderTargetSize(1920, 1080, color_pitch, 8, &color, 3) &&
Require("MsaaCompatibility", "color mismatch", color.align == 0x10000 && color.size == 0x07f80000,
!color_msaa_single_sample_compatible(2, 1), "AGC 8x R16G16B16A16 color footprint was not preserved");
"mismatched color sample/fragment state was silently admitted");
Require("MsaaCompatibility", "color eight fragments", TileSizeAlign depth{};
!color_msaa_single_sample_compatible(3, 3), TileSizeAlign stencil{};
"unsupported eight-sample color state was silently admitted"); TileSizeAlign htile{};
std::printf("[host] %-32s ok\n", "MsaaCompatibility"); Require(
"NativeMsaaState", "8x depth/stencil footprint",
TileGetDepthPitch(1920, 4, 3) == 1920 &&
TileGetDepthSize(
1920, 1080, 0,
Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F),
Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), true,
&stencil, &htile, &depth, 3) &&
depth.align == 0x10000 && depth.size == 0x03fc0000 &&
stencil.align == 0x10000 && stencil.size == 0x010e0000 &&
htile.align == 0x8000 && htile.size == 0x00030000,
"AGC 8x depth/stencil or fragment-independent HTile footprint regressed");
std::printf("[host] %-32s ok\n", "NativeMsaaState");
} }
void CheckDepthHtileStencilCompatibility() { void CheckDepthHtileStencilCompatibility() {
@@ -12837,34 +13133,39 @@ void CheckDepthTargetFootprints() {
uint32_t bytes_per_element; uint32_t bytes_per_element;
bool stencil; bool stencil;
bool supported; bool supported;
bool readback;
}; };
constexpr TargetFormatCase target_cases[] = { constexpr TargetFormatCase target_cases[] = {
{"D16", vk::Format::eD16Unorm, {"D16", vk::Format::eD16Unorm,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false, true,
true}, true},
{"D16S8", vk::Format::eD16UnormS8Uint, {"D16S8", vk::Format::eD16UnormS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true,
true},
{"D16 via D24S8", vk::Format::eD24UnormS8Uint, {"D16 via D24S8", vk::Format::eD24UnormS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true,
false},
{"D16 via D32S8", vk::Format::eD32SfloatS8Uint, {"D16 via D32S8", vk::Format::eD32SfloatS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true,
false},
{"D32", vk::Format::eD32Sfloat, {"D32", vk::Format::eD32Sfloat,
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, false, Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, false, true,
true}, true},
{"D32S8", vk::Format::eD32SfloatS8Uint, {"D32S8", vk::Format::eD32SfloatS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, true}, Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, true,
true},
{"D16 plus stencil mismatch", vk::Format::eD16Unorm, {"D16 plus stencil mismatch", vk::Format::eD16Unorm,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, false,
false}, false},
{"fallback without stencil", vk::Format::eD24UnormS8Uint, {"fallback without stencil", vk::Format::eD24UnormS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false,
false}, false, false},
{"D32 guest via D24", vk::Format::eD24UnormS8Uint, {"D32 guest via D24", vk::Format::eD24UnormS8Uint,
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, false,
false}, false},
{"D16 byte mismatch", vk::Format::eD16Unorm, {"D16 byte mismatch", vk::Format::eD16Unorm,
Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 4, false, Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 4, false,
false}, false, false},
}; };
for (const auto &test : target_cases) { for (const auto &test : target_cases) {
DepthTargetInfo target{}; DepthTargetInfo target{};
@@ -12874,9 +13175,23 @@ void CheckDepthTargetFootprints() {
target.stencil_address = test.stencil ? 0x10000 : 0; target.stencil_address = test.stencil ? 0x10000 : 0;
target.stencil_size = test.stencil ? 0x10000 : 0; target.stencil_size = test.stencil ? 0x10000 : 0;
Require("DepthTargetFootprints", test.name, Require("DepthTargetFootprints", test.name,
IsSupportedDepthTargetFormat(target) == test.supported, IsSupportedDepthTargetFormat(target) == test.supported &&
"host/guest depth format policy changed"); IsSupportedDepthReadbackFormat(target) == test.readback,
"host/guest depth format or exact readback policy changed");
} }
DepthTargetInfo compressed_stencil{};
compressed_stencil.format = vk::Format::eD32SfloatS8Uint;
compressed_stencil.guest_format =
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
compressed_stencil.bytes_per_element = 4;
compressed_stencil.stencil_address = 0x10000;
compressed_stencil.stencil_size = 0x10000;
compressed_stencil.stencil_htile_compressed = true;
Require(
"DepthTargetFootprints", "compressed stencil readback hard guard",
IsSupportedDepthTargetFormat(compressed_stencil) &&
!IsSupportedDepthReadbackFormat(compressed_stencil),
"compressed stencil unexpectedly entered the raw-plane readback path");
struct TransferPlaneCase { struct TransferPlaneCase {
const char *name; const char *name;
vk::Format attachment; vk::Format attachment;
@@ -13098,6 +13413,20 @@ void CheckHtileClearTargetResolution() {
resolved.address == derived_z16s8.htile_data_base_addr && resolved.address == derived_z16s8.htile_data_base_addr &&
resolved.size == htile_size.size, resolved.size == htile_size.size,
"extent-derived Z16S8 HTile target was rejected"); "extent-derived Z16S8 HTile target was rejected");
auto derived_8x = derived;
derived_8x.z_info.num_samples = 3;
auto malformed_fragments = derived;
malformed_fragments.z_info.num_samples = 4;
Require("HtileClearTargetResolution", "fragment-independent metadata plane",
ResolveHtileClearTarget(derived_8x, htile_size.size, &resolved) &&
resolved.address == derived_8x.htile_data_base_addr &&
resolved.size == htile_size.size &&
render_sample_count(derived_8x.z_info.num_samples) == 8 &&
!ResolveHtileClearTarget(malformed_fragments, htile_size.size,
&resolved),
"HTile clear was coupled to attachment MSAA support or admitted an "
"invalid fragment field");
std::printf("[host] %-32s ok\n", "HtileClearTargetResolution"); std::printf("[host] %-32s ok\n", "HtileClearTargetResolution");
} }
@@ -13154,8 +13483,8 @@ void CheckHostDmaMetadataReuse() {
fault_context.texture = &texture_cache; fault_context.texture = &texture_cache;
buffer_cache.SetTextureCache(texture_cache); buffer_cache.SetTextureCache(texture_cache);
page_manager.OnGpuMap(base, allocation_size); page_manager.OnGpuMap(base, allocation_size);
texture_cache.RegisterMeta(base, metadata_size);
auto *ctx = reinterpret_cast<GraphicContext *>(1); auto *ctx = reinterpret_cast<GraphicContext *>(1);
texture_cache.RegisterMeta(ctx, base, metadata_size);
Require("HostDmaMetadataReuse", "clear", texture_cache.ClearMeta(base), Require("HostDmaMetadataReuse", "clear", texture_cache.ClearMeta(base),
"metadata clear setup failed"); "metadata clear setup failed");
@@ -13400,7 +13729,7 @@ int main(int argc, char **argv) {
CheckMetadataReuseDescriptors(); CheckMetadataReuseDescriptors();
CheckImageOverlapResolution(); CheckImageOverlapResolution();
CheckQueryRegionAggregation(); CheckQueryRegionAggregation();
CheckMsaaCompatibility(); CheckNativeMsaaState();
CheckDepthHtileStencilCompatibility(); CheckDepthHtileStencilCompatibility();
CheckStencilAttachmentAccess(); CheckStencilAttachmentAccess();
CheckDepthTargetFootprints(); CheckDepthTargetFootprints();