mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
graphics: add native PS5 MSAA and safe image allocation reuse
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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 };
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user