Support Standard64KB color render targets

This commit is contained in:
nmzik
2026-07-17 12:57:40 +02:00
parent 1416131a0a
commit a290a06ea5
8 changed files with 359 additions and 29 deletions
+61
View File
@@ -1476,6 +1476,67 @@ void TileConvertTiledToLinearStandard64KB32(void* dst, const void* src, uint32_t
}
}
void TileConvertLinearToTiledStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size) {
KYTY_PROFILER_FUNCTION();
const auto rows = static_cast<uint64_t>(height == 0 ? 0 : height - 1u);
const auto expected_pitch = (static_cast<uint64_t>(width) + 127u) & ~uint64_t {127u};
if (pitch != expected_pitch || size < (rows * pitch + width) * sizeof(uint32_t)) {
EXIT("invalid linear-to-Standard64KB32 conversion, dst=%p src=%p extent=%ux%u "
"pitch=%u size=0x%016" PRIx64 "\n",
dst, src, width, height, pitch, size);
}
auto* dst32 = static_cast<uint32_t*>(dst);
const auto* src32 = static_cast<const uint32_t*>(src);
const auto& tables = GetStandard64KB32Tables();
const auto blocks_per_row = (static_cast<uint64_t>(pitch) + 127u) >> 7u;
const auto block_rows = (static_cast<uint64_t>(height) + 127u) >> 7u;
if (blocks_per_row > UINT64_MAX / block_rows / 65536u ||
size != blocks_per_row * block_rows * 65536u) {
EXIT("invalid Standard64KB32 allocation, extent=%ux%u pitch=%u size=0x%016" PRIx64
" blocks=%" PRIu64 "x%" PRIu64 "\n",
width, height, pitch, size, blocks_per_row, block_rows);
}
const auto* x_words = tables.x_words.data();
const auto* y_words = tables.y_words.data();
std::memset(dst32, 0, static_cast<size_t>(size));
for (uint32_t block_y = 0; block_y < height; block_y += 128u) {
const uint32_t block_height = std::min(128u, height - block_y);
for (uint32_t block_x = 0; block_x < width; block_x += 128u) {
const uint32_t block_width = std::min(128u, width - block_x);
const uint64_t block_base =
(((static_cast<uint64_t>(block_y) >> 7u) * blocks_per_row) + (block_x >> 7u))
<< 14u;
if (block_width == 128u && block_height == 128u) {
for (uint32_t local_y = 0; local_y < 128u; local_y++) {
const uint64_t src_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t dst_row = block_base + y_words[local_y];
for (uint32_t local_x = 0; local_x < 128u; local_x += 4u) {
dst32[dst_row + x_words[local_x + 0u]] = src32[src_row + local_x + 0u];
dst32[dst_row + x_words[local_x + 1u]] = src32[src_row + local_x + 1u];
dst32[dst_row + x_words[local_x + 2u]] = src32[src_row + local_x + 2u];
dst32[dst_row + x_words[local_x + 3u]] = src32[src_row + local_x + 3u];
}
}
continue;
}
for (uint32_t local_y = 0; local_y < block_height; local_y++) {
const uint64_t src_row =
(static_cast<uint64_t>(block_y + local_y) * pitch) + block_x;
const uint64_t dst_row = block_base + y_words[local_y];
for (uint32_t local_x = 0; local_x < block_width; local_x++) {
dst32[dst_row + x_words[local_x]] = src32[src_row + local_x];
}
}
}
}
}
void TileConvertTiledToLinearStandard64KB16(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size, uint32_t src_x, uint32_t src_y) {
+2
View File
@@ -53,6 +53,8 @@ void TileConvertTiledToLinearStandard64KB32(void* dst, const void* src, uint32_t
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size = 0, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertLinearToTiledStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size);
void TileConvertTiledToLinearStandard64KB16(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size = 0, uint32_t src_x = 0,
@@ -373,9 +373,12 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
uint32_t pitch = 0;
uint64_t size = 0;
bool tile = false;
const bool standard64 =
rt.attrib3.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
switch (rt.attrib3.tile_mode) {
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget):
tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode);
break;
@@ -391,10 +394,32 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
if (bytes_per_element == 0) {
EXIT("render-target format has no valid element size\n");
}
if (standard64 &&
(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.attrib.num_samples != 0 ||
rt.attrib.num_fragments != 0 || bytes_per_element != 4 ||
rt.pitch.pitch_div8_minus1 != 0 || (rt.base.addr & 0xffffu) != 0 ||
rt.info.fmask_compression_enable || rt.info.fmask_data_compression_disable ||
rt.info.fmask_one_frag_mode || rt.info.cmask_fast_clear_enable ||
rt.info.dcc_compression_enable || rt.info.cmask_is_linear != 0 ||
rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode || rt.cmask.addr != 0 ||
rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) {
EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64
" 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",
rt.base.addr, rt.attrib3.dimension, rt.attrib3.depth, levels, view.base_layer,
view.image_layers,
rt.attrib.num_samples, rt.attrib.num_fragments, bytes_per_element, rt.cmask.addr,
rt.fmask.addr, rt.dcc_addr.addr);
}
if (rt.pitch.pitch_div8_minus1 != 0) {
pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u;
} else if (tile) {
pitch = TileGetRenderTargetPitch(width, bytes_per_element);
pitch = standard64
? TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float),
width, levels, rt.attrib3.tile_mode)
: TileGetRenderTargetPitch(width, bytes_per_element);
if (pitch == 0) {
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
}
@@ -404,10 +429,18 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
if (tile) {
TileSizeAlign layout {};
const bool valid_layout =
levels == 1 ? TileGetRenderTargetSize(width, height, pitch, bytes_per_element, &layout)
: TileGetRenderTargetMipLayout(width, height, pitch, bytes_per_element,
levels, &layout, nullptr, nullptr);
bool valid_layout = false;
if (standard64) {
TileGetTextureSize(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), width,
height, pitch, levels, rt.attrib3.tile_mode, &layout, nullptr, nullptr);
valid_layout = layout.size != 0 && layout.align == 65536;
} else {
valid_layout =
levels == 1
? TileGetRenderTargetSize(width, height, pitch, bytes_per_element, &layout)
: TileGetRenderTargetMipLayout(width, height, pitch, bytes_per_element, levels,
&layout, nullptr, nullptr);
}
if (!valid_layout) {
EXIT("unsupported render-target layout: %ux%u pitch=%u bytes=%u levels=%u\n", width,
height, pitch, bytes_per_element, levels);
@@ -430,7 +463,14 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
EXIT("render-target backing range is invalid\n");
}
auto video_image = Presentation::DisplayBufferFind(rt.base.addr, true);
auto video_image = Presentation::DisplayBufferFind(rt.base.addr, true);
if (video_image.image != nullptr &&
!IsSupportedDisplayRenderTargetTileMode(rt.attrib3.tile_mode)) {
EXIT("unsupported display render-target tile mode: tile=%u expected=%u addr=0x%010" PRIx64
" backing_size=0x%016" PRIx64 " video_size=0x%016" PRIx64 "\n",
rt.attrib3.tile_mode, Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget),
rt.base.addr, backing_size, video_image.size);
}
bool render_to_texture = view.base_layer != 0 || video_image.image == nullptr;
if (!render_to_texture && (levels != 1 || rt.view.current_mip_level != 0)) {
EXIT("mipmapped display render targets are unsupported\n");
-9
View File
@@ -369,15 +369,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
if (!RenderIsColorTileMode(rt.attrib3.tile_mode)) {
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n", rt.attrib3.tile_mode);
}
if (rt.attrib3.tile_mode != 0x0000001b) {
static bool logged = false;
if (!logged) {
LOGF("RenderTarget: temporary: using generic PS5 color tile fallback for "
"tile_mode=0x%08" PRIx32 "\n",
rt.attrib3.tile_mode);
logged = true;
}
}
if (!RenderIsColorDimension(rt.attrib3.dimension)) {
EXIT("unknown PS5 render-target dimension: 0x%08" PRIx32 "\n", rt.attrib3.dimension);
}
@@ -360,6 +360,30 @@ SelectStorageSampledViewShape(uint32_t type, uint32_t depth, uint32_t backing_la
}
}
[[nodiscard]] inline constexpr bool IsSupportedDisplayRenderTargetTileMode(
uint32_t tile_mode) noexcept {
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
}
[[nodiscard]] inline constexpr bool
IsSupportedStandard64RenderTarget(const RenderTargetInfo& info) noexcept {
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
info.address == 0 || (info.address & 0xffffu) != 0 || info.width == 0 || info.height == 0 ||
info.bytes_per_element != 4 || info.levels != 1 || info.layers != 1) {
return false;
}
const auto expected_pitch = (static_cast<uint64_t>(info.width) + 127u) & ~uint64_t {127u};
const auto padded_height = (static_cast<uint64_t>(info.height) + 127u) & ~uint64_t {127u};
return expected_pitch <= UINT32_MAX && info.pitch == expected_pitch &&
expected_pitch <= UINT64_MAX / padded_height / info.bytes_per_element &&
info.size == expected_pitch * padded_height * info.bytes_per_element;
}
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const RenderTargetInfo& info) noexcept {
return info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
IsSupportedStandard64RenderTarget(info);
}
[[nodiscard]] inline constexpr DepthTransitionSource
SelectDepthTransitionSource(bool depth_load_clear, bool sampled_native_available,
bool sampled_cpu_dirty, bool sampled_buffer_modified,
+36 -11
View File
@@ -415,9 +415,8 @@ struct TextureCache::ReadbackWorker {
cached.info, Prospero::SurfaceFormat(cached.info.format),
Prospero::NumBytesPerElement(cached.info.format))
: MakeColorImageTransferInfo(cached.target);
const bool linear = info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
const bool tiled =
target && info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
const bool linear = info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
const bool tiled = target && IsTiledRenderTarget(cached.target);
bool single_layer_storage = false;
if (storage) {
switch (static_cast<Prospero::ImageType>(cached.info.type)) {
@@ -975,14 +974,16 @@ void UploadRenderTargetLayers(GraphicContext* ctx, RenderTextureVulkanImage* ima
}
const auto slice_size = info.size / info.layers;
const auto upload_size = slice_size * layer_count;
if (info.levels > 1 || info.layers > 1) {
const bool standard64 = IsSupportedStandard64RenderTarget(info);
if (standard64 || info.levels > 1 || info.layers > 1) {
const auto format = RenderTargetTransferFormat(info.bytes_per_element);
auto layout = TextureCalcUploadLayout(format, info.width, info.height, info.levels,
layer_count, info.pitch, info.tile_mode, upload_size,
false, false, false, "TextureCache render target");
const bool tiled =
const bool render_target_tiled =
info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
if ((tiled && !layout.fmt_tiled_render_target) || layout.pitch != info.pitch) {
if ((render_target_tiled && !layout.fmt_tiled_render_target) ||
(standard64 && !layout.fmt_tiled_standard64kb) || layout.pitch != info.pitch) {
EXIT("TextureCache: unsupported render-target mip upload layout, pitch=%u/%u tile=%u\n",
info.pitch, layout.pitch, info.tile_mode);
}
@@ -2074,13 +2075,15 @@ StorageTextureVulkanImage* TextureCache::FindStorageTexture(CommandBuffer* com
RenderTextureVulkanImage* TextureCache::FindRenderTarget(CommandBuffer* command,
GraphicContext* ctx,
const RenderTargetInfo& info) {
const bool standard64 = IsSupportedStandard64RenderTarget(info);
if (command == nullptr || ctx == nullptr || info.address == 0 || info.size == 0 ||
info.address >= TRACKER_ADDRESS_SIZE || info.size > TRACKER_ADDRESS_SIZE - info.address ||
info.format == VK_FORMAT_UNDEFINED || info.width == 0 || info.height == 0 ||
info.pitch < info.width || info.bytes_per_element == 0 || info.levels == 0 ||
info.levels > 16 || info.layers == 0 || info.size % info.layers != 0 ||
(info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear) &&
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget))) {
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
!standard64)) {
EXIT("TextureCache: invalid render-target request, ctx=%p addr=0x%016" PRIx64
" size=0x%016" PRIx64 " extent=%ux%u pitch=%u bpe=%u tile=%u format=%d\n",
static_cast<const void*>(ctx), info.address, info.size, info.width, info.height,
@@ -2100,6 +2103,19 @@ RenderTextureVulkanImage* TextureCache::FindRenderTarget(CommandBuffer*
info.bytes_per_element, info.levels, info.tile_mode);
}
}
if (standard64) {
const auto format = RenderTargetTransferFormat(info.bytes_per_element);
const auto expected_pitch =
TileGetTexturePitch(format, info.width, info.levels, info.tile_mode);
TileSizeAlign layout {};
TileGetTextureSize(format, info.width, info.height, expected_pitch, info.levels,
info.tile_mode, &layout, nullptr, nullptr);
if (expected_pitch != info.pitch || layout.align != 65536 || layout.size != info.size) {
EXIT("TextureCache: invalid Standard64KB render-target layout,"
" size=0x%016" PRIx64 " expected=0x%08x align=0x%08x pitch=%u/%u\n",
info.size, layout.size, layout.align, info.pitch, expected_pitch);
}
}
const auto rows = static_cast<uint64_t>(info.height - 1);
if (rows > (UINT64_MAX - info.width) / info.pitch) {
EXIT("TextureCache: render-target element count overflow, extent=%ux%u pitch=%u\n",
@@ -3085,12 +3101,21 @@ void TextureCache::PrepareHostWrite(uint64_t vaddr, uint64_t size) {
void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) {
const auto& target = cached.target;
const bool linear = target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
const bool tiled =
target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
const bool tiled = IsTiledRenderTarget(target);
const auto rows = static_cast<uint64_t>(target.height - 1);
TileSizeAlign exact {};
const bool single_slice = TileGetRenderTargetSize(target.width, target.height, target.pitch,
target.bytes_per_element, &exact);
bool single_slice = false;
if (IsSupportedStandard64RenderTarget(target)) {
const auto format = RenderTargetTransferFormat(target.bytes_per_element);
const auto expected_pitch =
TileGetTexturePitch(format, target.width, target.levels, target.tile_mode);
TileGetTextureSize(format, target.width, target.height, expected_pitch, target.levels,
target.tile_mode, &exact, nullptr, nullptr);
single_slice = target.pitch == expected_pitch && exact.align == 65536 && exact.size != 0;
} else {
single_slice = TileGetRenderTargetSize(target.width, target.height, target.pitch,
target.bytes_per_element, &exact);
}
const bool layered_size = target.layers != 0 && single_slice &&
exact.size <= UINT64_MAX / target.layers &&
exact.size * target.layers == target.size;
+8 -3
View File
@@ -214,7 +214,7 @@ void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info) const {
if (dst == nullptr || src == nullptr || info.address == 0 || info.size == 0 ||
info.width == 0 || info.height == 0 || info.pitch < info.width ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
!IsTiledRenderTarget(info) ||
info.levels != 1 || info.layers == 0 || info.size % info.layers != 0 ||
!IsSupportedRenderTargetElementSize(info.bytes_per_element)) {
EXIT("Tiler: unsupported render-target tile, dst=%p src=%p "
@@ -227,8 +227,13 @@ void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info)
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;
TileConvertLinearToTiledRenderTarget(guest_slice, linear_slice, info.width, info.height,
info.pitch, info.bytes_per_element, slice_size);
if (IsSupportedStandard64RenderTarget(info)) {
TileConvertLinearToTiledStandard64KB32(guest_slice, linear_slice, info.width,
info.height, info.pitch, slice_size);
} else {
TileConvertLinearToTiledRenderTarget(guest_slice, linear_slice, info.width, info.height,
info.pitch, info.bytes_per_element, slice_size);
}
}
}