mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
render: apply DCC clears before binding images
Co-authored-by: Techx3 <56268269+Techx3@users.noreply.github.com> Co-authored-by: Brandon Strong <brandostrong@gmail.com>
This commit is contained in:
@@ -1221,6 +1221,31 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||||
|
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||||
|
TileSizeAlign& total_size, uint32_t num_fragments_log2) {
|
||||||
|
total_size = {};
|
||||||
|
if (width == 0 || height == 0 || slices == 0 || levels != 1 || num_fragments_log2 != 0 ||
|
||||||
|
!std::has_single_bit(bytes_per_element) || bytes_per_element > 16 ||
|
||||||
|
(tile != Prospero::TileMode::kRenderTarget && tile != Prospero::TileMode::kDepth)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Gen5 color metadata uses 4 KiB blocks, with one byte per 256 bytes of color data.
|
||||||
|
// Metadata stays thin for volume surfaces, so every depth slice has its own block raster.
|
||||||
|
const uint32_t coverage_bits = 20u - std::countr_zero(bytes_per_element);
|
||||||
|
const uint32_t block_width = 1u << ((coverage_bits + 1u) / 2u);
|
||||||
|
const uint32_t block_height = 1u << (coverage_bits / 2u);
|
||||||
|
const uint64_t blocks_x = (static_cast<uint64_t>(width) + block_width - 1u) / block_width;
|
||||||
|
const uint64_t blocks_y = (static_cast<uint64_t>(height) + block_height - 1u) / block_height;
|
||||||
|
const uint64_t blocks = blocks_x * blocks_y;
|
||||||
|
if (blocks > UINT32_MAX / 4096u / slices) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total_size.size = static_cast<uint32_t>(blocks * slices * 4096u);
|
||||||
|
total_size.align = 4096;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
@@ -122,6 +122,9 @@ uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
|
|||||||
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);
|
uint32_t num_fragments_log2 = 0);
|
||||||
|
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||||
|
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||||
|
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,
|
||||||
|
|||||||
+183
-45
@@ -32,6 +32,78 @@ namespace {
|
|||||||
|
|
||||||
constexpr uint64_t NumFramesBeforeRemoval = 32;
|
constexpr uint64_t NumFramesBeforeRemoval = 32;
|
||||||
|
|
||||||
|
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, vk::Format format,
|
||||||
|
uint32_t fill, vk::ClearColorValue& clear) {
|
||||||
|
const auto code = static_cast<uint8_t>(fill);
|
||||||
|
if (fill != static_cast<uint32_t>(code) * 0x01010101u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& metadata = desc.info.metadata;
|
||||||
|
if (code == 0x20) {
|
||||||
|
// Clear-to-register is a color-buffer operation; the texture pipe cannot decode it.
|
||||||
|
return desc.type == TextureCache::BindingType::RenderTarget &&
|
||||||
|
metadata.dcc_clear_register_valid &&
|
||||||
|
DecodePackedColorClear(format, metadata.dcc_clear_word, clear);
|
||||||
|
}
|
||||||
|
if (code != 0x00 && code != 0x40 && code != 0x80 && code != 0xc0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
clear = {};
|
||||||
|
if (code == 0x00) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (format) {
|
||||||
|
case vk::Format::eR8Unorm:
|
||||||
|
case vk::Format::eR8G8Unorm:
|
||||||
|
case vk::Format::eR8G8B8A8Unorm:
|
||||||
|
case vk::Format::eR8G8B8A8Srgb:
|
||||||
|
case vk::Format::eB8G8R8A8Unorm:
|
||||||
|
case vk::Format::eB8G8R8A8Srgb:
|
||||||
|
case vk::Format::eA2B10G10R10UnormPack32:
|
||||||
|
case vk::Format::eA2R10G10B10UnormPack32:
|
||||||
|
case vk::Format::eR5G6B5UnormPack16:
|
||||||
|
case vk::Format::eA1R5G5B5UnormPack16:
|
||||||
|
case vk::Format::eR4G4B4A4UnormPack16:
|
||||||
|
case vk::Format::eR16Unorm:
|
||||||
|
case vk::Format::eR16G16Unorm:
|
||||||
|
case vk::Format::eR16G16B16A16Unorm:
|
||||||
|
case vk::Format::eR16Sfloat:
|
||||||
|
case vk::Format::eR16G16Sfloat:
|
||||||
|
case vk::Format::eR16G16B16A16Sfloat:
|
||||||
|
case vk::Format::eR32Sfloat:
|
||||||
|
case vk::Format::eR32G32Sfloat:
|
||||||
|
case vk::Format::eR32G32B32A32Sfloat:
|
||||||
|
case vk::Format::eB10G11R11UfloatPack32: break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
const float rgb = (code & 0x80u) != 0 ? 1.0f : 0.0f;
|
||||||
|
const float alpha = (code & 0x40u) != 0 ? 1.0f : 0.0f;
|
||||||
|
std::array<float, 4> channels {rgb, rgb, rgb, alpha};
|
||||||
|
if (!metadata.dcc_alpha_msb) {
|
||||||
|
std::swap(channels[0], channels[3]);
|
||||||
|
}
|
||||||
|
// DCC clear decoding clamps missing lanes before applying the format swizzle.
|
||||||
|
const auto components = vk::componentCount(format);
|
||||||
|
if (components == 1) {
|
||||||
|
channels[0] = channels[3];
|
||||||
|
} else if (components == 2) {
|
||||||
|
channels[1] = channels[3];
|
||||||
|
}
|
||||||
|
switch (format) {
|
||||||
|
case vk::Format::eB8G8R8A8Unorm:
|
||||||
|
case vk::Format::eB8G8R8A8Srgb:
|
||||||
|
case vk::Format::eA2R10G10B10UnormPack32:
|
||||||
|
case vk::Format::eA1R5G5B5UnormPack16:
|
||||||
|
case vk::Format::eR5G6B5UnormPack16: std::swap(channels[0], channels[2]); break;
|
||||||
|
case vk::Format::eR4G4B4A4UnormPack16:
|
||||||
|
std::reverse(channels.begin(), channels.end());
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
clear.float32 = channels;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] const char* BindingTypeName(TextureCache::BindingType type) {
|
[[nodiscard]] const char* BindingTypeName(TextureCache::BindingType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TextureCache::BindingType::Texture: return "Texture";
|
case TextureCache::BindingType::Texture: return "Texture";
|
||||||
@@ -1062,6 +1134,56 @@ void TextureCache::InitializeImage(ImageId id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureCache::PrepareDccClear(ImageId id, const ImageDesc& desc) {
|
||||||
|
if (desc.info.metadata.kind != ImageMetadataKind::Dcc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& image = m_slot_images[id];
|
||||||
|
image.info.metadata = desc.info.metadata;
|
||||||
|
auto [entry, inserted] = m_surface_metas.try_emplace(
|
||||||
|
desc.info.metadata.range.address, MetaDataInfo {.type = MetaDataInfo::Type::Dcc});
|
||||||
|
auto& metadata = entry->second;
|
||||||
|
if (!inserted && metadata.type == MetaDataInfo::Type::PendingDcc) {
|
||||||
|
metadata.type = MetaDataInfo::Type::Dcc;
|
||||||
|
} else if (metadata.type != MetaDataInfo::Type::Dcc) {
|
||||||
|
EXIT("TextureCache: image reuses non-DCC metadata\n");
|
||||||
|
}
|
||||||
|
if (metadata.clear_mask == 0 || image.info.resources.levels != 1 ||
|
||||||
|
desc.info.metadata.range.size == 0 || metadata.fill_size < desc.info.metadata.range.size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vk::ClearValue clear {};
|
||||||
|
if (!DecodeDccClear(desc, image.backing.format, metadata.fill_value, clear.color)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& view = desc.view_info;
|
||||||
|
const bool volume_texture = image.info.IsVolume() && view.type == vk::ImageViewType::e3D;
|
||||||
|
const auto first = volume_texture ? 0u : view.base_layer;
|
||||||
|
const auto count = volume_texture ? std::max(image.info.extent.depth >> view.base_level, 1u)
|
||||||
|
: view.layer_count;
|
||||||
|
if (first >= 32 || count > 32 - first) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The metadata fill covers the complete allocation. Consume each layer only after its
|
||||||
|
// native image contents exist; already materialized layers may have been rendered since.
|
||||||
|
for (uint32_t layer = first; layer < first + count;) {
|
||||||
|
if ((metadata.clear_mask & (1u << layer)) == 0) {
|
||||||
|
layer++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto start = layer;
|
||||||
|
uint32_t mask = 0;
|
||||||
|
do {
|
||||||
|
mask |= 1u << layer++;
|
||||||
|
} while (layer < first + count && (metadata.clear_mask & (1u << layer)) != 0);
|
||||||
|
ClearImage(m_scheduler.Current(), id,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count, start,
|
||||||
|
layer - start},
|
||||||
|
clear);
|
||||||
|
metadata.clear_mask &= ~mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TextureCache::RefreshImage(ImageId id) {
|
void TextureCache::RefreshImage(ImageId id) {
|
||||||
TrackImage(id);
|
TrackImage(id);
|
||||||
auto& image = m_slot_images[id];
|
auto& image = m_slot_images[id];
|
||||||
@@ -1253,6 +1375,7 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) {
|
|||||||
image.MarkGpuModified();
|
image.MarkGpuModified();
|
||||||
}
|
}
|
||||||
if (!image.info.data.Empty()) {
|
if (!image.info.data.Empty()) {
|
||||||
|
PrepareDccClear(id, desc);
|
||||||
RefreshImage(id);
|
RefreshImage(id);
|
||||||
}
|
}
|
||||||
switch (desc.type) {
|
switch (desc.type) {
|
||||||
@@ -1285,20 +1408,8 @@ vk::ImageView TextureCache::FindRenderTarget(ImageId id, const ImageDesc& desc)
|
|||||||
TouchImage(image);
|
TouchImage(image);
|
||||||
image.MarkGpuModified();
|
image.MarkGpuModified();
|
||||||
image.usage.render_target = true;
|
image.usage.render_target = true;
|
||||||
|
PrepareDccClear(id, desc);
|
||||||
RefreshImage(id);
|
RefreshImage(id);
|
||||||
// DCC uses a separate metadata allocation. Register it when the color target is bound,
|
|
||||||
// matching the point where CMask/FMask will be registered. Preserve a PendingDcc entry
|
|
||||||
// because the metadata fill may have run before this bind.
|
|
||||||
if (desc.info.metadata.kind == ImageMetadataKind::Dcc) {
|
|
||||||
image.info.metadata = desc.info.metadata;
|
|
||||||
auto [metadata, inserted] = m_surface_metas.try_emplace(
|
|
||||||
desc.info.metadata.range.address, MetaDataInfo {.type = MetaDataInfo::Type::Dcc});
|
|
||||||
if (!inserted && metadata->second.type == MetaDataInfo::Type::PendingDcc) {
|
|
||||||
metadata->second.type = MetaDataInfo::Type::Dcc;
|
|
||||||
} else if (!inserted && metadata->second.type != MetaDataInfo::Type::Dcc) {
|
|
||||||
EXIT("TextureCache: color target reuses non-DCC metadata\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CommitGpuWrite(image);
|
CommitGpuWrite(image);
|
||||||
TrackImageDownload(id, image);
|
TrackImageDownload(id, image);
|
||||||
const auto view = image.FindView(desc.view_info);
|
const auto view = image.FindView(desc.view_info);
|
||||||
@@ -1422,8 +1533,8 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
|||||||
}
|
}
|
||||||
clear.depthStencil.stencil = stencil_clear;
|
clear.depthStencil.stencil = stencil_clear;
|
||||||
}
|
}
|
||||||
ClearImage(command, selected, {aspect, 0, image.info.resources.levels, 0, image.backing.layers},
|
ClearImage(command, selected,
|
||||||
clear);
|
{aspect, 0, image.info.resources.levels, 0, image.info.TransferLayers()}, clear);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,27 +1543,70 @@ void TextureCache::ClearImage(CommandBuffer& command, ImageId id,
|
|||||||
auto& image = m_slot_images[id];
|
auto& image = m_slot_images[id];
|
||||||
const auto aspects = image.info.IsDepth() ? ImageViewOps::DepthAspectMask(image.backing.format)
|
const auto aspects = image.info.IsDepth() ? ImageViewOps::DepthAspectMask(image.backing.format)
|
||||||
: vk::ImageAspectFlagBits::eColor;
|
: vk::ImageAspectFlagBits::eColor;
|
||||||
EXIT_IF(command.IsInvalid() || image.depth_id || !range.aspectMask ||
|
EXIT_IF(range.baseMipLevel >= image.info.resources.levels);
|
||||||
range.levelCount == 0 || range.baseMipLevel >= image.info.resources.levels ||
|
const auto layers = image.info.IsVolume()
|
||||||
|
? std::max(image.info.extent.depth >> range.baseMipLevel, 1u)
|
||||||
|
: image.backing.layers;
|
||||||
|
EXIT_IF(command.IsInvalid() || image.depth_id || !range.aspectMask || range.levelCount == 0 ||
|
||||||
range.levelCount > image.info.resources.levels - range.baseMipLevel ||
|
range.levelCount > image.info.resources.levels - range.baseMipLevel ||
|
||||||
range.layerCount == 0 || range.baseArrayLayer >= image.backing.layers ||
|
range.layerCount == 0 || range.baseArrayLayer >= layers ||
|
||||||
range.layerCount > image.backing.layers - range.baseArrayLayer ||
|
range.layerCount > layers - range.baseArrayLayer ||
|
||||||
(range.aspectMask & aspects) != range.aspectMask);
|
(range.aspectMask & aspects) != range.aspectMask);
|
||||||
if (image.IsBufferModified() || image.IsCpuDirty()) {
|
const bool full_image = range.aspectMask == aspects && range.baseMipLevel == 0 &&
|
||||||
|
range.levelCount == image.info.resources.levels &&
|
||||||
|
range.baseArrayLayer == 0 && range.layerCount == layers;
|
||||||
|
TrackImage(id);
|
||||||
|
if (!full_image && (image.IsBufferModified() || image.IsCpuDirty())) {
|
||||||
InitializeImage(id);
|
InitializeImage(id);
|
||||||
if (image.info.samples == 1 && (image.IsBufferModified() || image.IsCpuDirty())) {
|
if (image.info.samples == 1 && (image.IsBufferModified() || image.IsCpuDirty())) {
|
||||||
EXIT("TextureCache: image clear retained guest ownership\n");
|
EXIT("TextureCache: image clear retained guest ownership\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
command.EndRendering();
|
command.EndRendering();
|
||||||
|
if (image.info.IsVolume() && !full_image) {
|
||||||
|
EXIT_NOT_IMPLEMENTED(range.aspectMask != vk::ImageAspectFlagBits::eColor ||
|
||||||
|
range.levelCount != 1);
|
||||||
|
ImageViewInfo view {};
|
||||||
|
view.format = image.backing.format;
|
||||||
|
view.type = range.layerCount == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||||
|
view.base_level = range.baseMipLevel;
|
||||||
|
view.base_layer = range.baseArrayLayer;
|
||||||
|
view.layer_count = range.layerCount;
|
||||||
|
view.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||||
|
image.Transit(vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
vk::AccessFlagBits2::eColorAttachmentWrite, {}, command.Handle());
|
||||||
|
vk::RenderingAttachmentInfo attachment {};
|
||||||
|
attachment.imageView = image.FindView(view);
|
||||||
|
attachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||||
|
attachment.loadOp = vk::AttachmentLoadOp::eClear;
|
||||||
|
attachment.storeOp = vk::AttachmentStoreOp::eStore;
|
||||||
|
attachment.clearValue = clear;
|
||||||
|
vk::RenderingInfo rendering {};
|
||||||
|
rendering.renderArea.extent = {
|
||||||
|
std::max(image.info.extent.width >> range.baseMipLevel, 1u),
|
||||||
|
std::max(image.info.extent.height >> range.baseMipLevel, 1u)};
|
||||||
|
rendering.layerCount = range.layerCount;
|
||||||
|
rendering.colorAttachmentCount = 1;
|
||||||
|
rendering.pColorAttachments = &attachment;
|
||||||
|
command.Handle().beginRendering(&rendering);
|
||||||
|
command.Handle().endRendering();
|
||||||
|
CommitGpuWrite(image);
|
||||||
|
return;
|
||||||
|
}
|
||||||
image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {},
|
image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {},
|
||||||
command.Handle());
|
command.Handle());
|
||||||
|
auto native_range = range;
|
||||||
|
if (image.info.IsVolume()) {
|
||||||
|
native_range.baseArrayLayer = 0;
|
||||||
|
native_range.layerCount = 1;
|
||||||
|
}
|
||||||
if (range.aspectMask == vk::ImageAspectFlagBits::eColor) {
|
if (range.aspectMask == vk::ImageAspectFlagBits::eColor) {
|
||||||
command.Handle().clearColorImage(image.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
command.Handle().clearColorImage(image.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||||
&clear.color, 1, &range);
|
&clear.color, 1, &native_range);
|
||||||
} else {
|
} else {
|
||||||
command.Handle().clearDepthStencilImage(
|
command.Handle().clearDepthStencilImage(image.backing.image,
|
||||||
image.backing.image, vk::ImageLayout::eTransferDstOptimal, &clear.depthStencil, 1, &range);
|
vk::ImageLayout::eTransferDstOptimal,
|
||||||
|
&clear.depthStencil, 1, &native_range);
|
||||||
}
|
}
|
||||||
CommitGpuWrite(image);
|
CommitGpuWrite(image);
|
||||||
}
|
}
|
||||||
@@ -1757,7 +1911,7 @@ bool TextureCache::ClearMeta(uint64_t address) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureCache::TryConsumeDccFill(uint64_t address, uint64_t size, uint32_t fill_value) {
|
void TextureCache::TrackDccFill(uint64_t address, uint64_t size, uint32_t fill_value) {
|
||||||
if (!GuestRange {address, size}.Valid()) {
|
if (!GuestRange {address, size}.Valid()) {
|
||||||
EXIT("TextureCache: invalid DCC fill range\n");
|
EXIT("TextureCache: invalid DCC fill range\n");
|
||||||
}
|
}
|
||||||
@@ -1778,31 +1932,15 @@ bool TextureCache::TryConsumeDccFill(uint64_t address, uint64_t size, uint32_t f
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
std::scoped_lock lock {m_lock};
|
std::scoped_lock lock {m_lock};
|
||||||
auto found = m_surface_metas.find(address);
|
// The guest dispatch still writes metadata. An unknown address remains PendingDcc until an
|
||||||
if (found == m_surface_metas.end()) {
|
// image descriptor confirms its role; never reinterpret CMask/FMask/HTile as DCC.
|
||||||
// This dispatch may precede color-target discovery. Retain it in the shared
|
const auto found = m_surface_metas.try_emplace(address).first;
|
||||||
// metadata map, but PendingDcc remains invisible to IsMeta until registration. Returning
|
if (found->second.type == MetaDataInfo::Type::PendingDcc ||
|
||||||
// false lets the guest dispatch execute and initialize memory while the type is uncertain.
|
found->second.type == MetaDataInfo::Type::Dcc) {
|
||||||
m_surface_metas.emplace(address, MetaDataInfo {.type = MetaDataInfo::Type::PendingDcc,
|
|
||||||
.clear_mask = dcc_clear_mask,
|
|
||||||
.fill_value = fill_value,
|
|
||||||
.fill_size = size});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (found->second.type == MetaDataInfo::Type::PendingDcc) {
|
|
||||||
found->second.clear_mask = dcc_clear_mask;
|
found->second.clear_mask = dcc_clear_mask;
|
||||||
found->second.fill_value = fill_value;
|
found->second.fill_value = fill_value;
|
||||||
found->second.fill_size = size;
|
found->second.fill_size = size;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if (found->second.type == MetaDataInfo::Type::Dcc) {
|
|
||||||
found->second.clear_mask = dcc_clear_mask;
|
|
||||||
found->second.fill_value = fill_value;
|
|
||||||
found->second.fill_size = size;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// The strict DCC path must not reinterpret a CMask/FMask/HTile allocation as DCC.
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureCache::TouchMeta(uint64_t address, uint32_t slice, bool is_clear) {
|
bool TextureCache::TouchMeta(uint64_t address, uint32_t slice, bool is_clear) {
|
||||||
|
|||||||
+5
-5
@@ -72,9 +72,8 @@ public:
|
|||||||
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice,
|
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice,
|
||||||
uint32_t* fill_value = nullptr);
|
uint32_t* fill_value = nullptr);
|
||||||
[[nodiscard]] bool ClearMeta(uint64_t address);
|
[[nodiscard]] bool ClearMeta(uint64_t address);
|
||||||
// Returns true when registered DCC absorbed the fill and the caller may skip the dispatch.
|
// Record deferred DCC state while the original guest dispatch writes the metadata.
|
||||||
// False may still record PendingDcc state, but the guest dispatch must execute.
|
void TrackDccFill(uint64_t address, uint64_t size, uint32_t fill_value);
|
||||||
[[nodiscard]] bool TryConsumeDccFill(uint64_t address, uint64_t size, uint32_t fill_value);
|
|
||||||
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
[[nodiscard]] bool TouchMeta(uint64_t address, uint32_t slice, bool is_clear);
|
||||||
|
|
||||||
void UnmapMemory(uint64_t address, uint64_t size);
|
void UnmapMemory(uint64_t address, uint64_t size);
|
||||||
@@ -88,7 +87,7 @@ private:
|
|||||||
|
|
||||||
struct MetaDataInfo {
|
struct MetaDataInfo {
|
||||||
// A guest metadata-fill dispatch may initialize DCC before its render target is bound.
|
// A guest metadata-fill dispatch may initialize DCC before its render target is bound.
|
||||||
// PendingDcc retains that exact fill until FindRenderTarget classifies the address,
|
// PendingDcc retains that exact fill until an image binding classifies the address,
|
||||||
// without exposing an unconfirmed buffer address to the normal metadata heuristics.
|
// without exposing an unconfirmed buffer address to the normal metadata heuristics.
|
||||||
// Keep all surface metadata in one entry so CMask/FMask can be
|
// Keep all surface metadata in one entry so CMask/FMask can be
|
||||||
// registered beside HTile and DCC without introducing parallel tracking paths.
|
// registered beside HTile and DCC without introducing parallel tracking paths.
|
||||||
@@ -138,6 +137,7 @@ private:
|
|||||||
ImageId cached);
|
ImageId cached);
|
||||||
[[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source);
|
[[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source);
|
||||||
void RefreshImage(ImageId id);
|
void RefreshImage(ImageId id);
|
||||||
|
void PrepareDccClear(ImageId id, const ImageDesc& desc);
|
||||||
void InitializeImage(ImageId id);
|
void InitializeImage(ImageId id);
|
||||||
[[nodiscard]] TextureTransferPlan
|
[[nodiscard]] TextureTransferPlan
|
||||||
BuildTextureTransfer(const Image& image, BindingType binding, TransferDirection direction) const;
|
BuildTextureTransfer(const Image& image, BindingType binding, TransferDirection direction) const;
|
||||||
@@ -147,7 +147,7 @@ private:
|
|||||||
uint64_t destination_size, DownloadPlan plan);
|
uint64_t destination_size, DownloadPlan plan);
|
||||||
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
||||||
void CommitGpuWrite(Image& image);
|
void CommitGpuWrite(Image& image);
|
||||||
// Caller holds m_lock. Clears only the selected aspects and subresources.
|
// Caller holds m_lock. Volume layer ranges select depth slices.
|
||||||
void ClearImage(CommandBuffer& command, ImageId id, const vk::ImageSubresourceRange& range,
|
void ClearImage(CommandBuffer& command, ImageId id, const vk::ImageSubresourceRange& range,
|
||||||
const vk::ClearValue& clear);
|
const vk::ClearValue& clear);
|
||||||
void PrepareImageCopy(Image& image);
|
void PrepareImageCopy(Image& image);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/profiler.h"
|
#include "common/profiler.h"
|
||||||
#include "graphics/guest_gpu/gpu_defs.h"
|
#include "graphics/guest_gpu/gpu_defs.h"
|
||||||
|
#include "graphics/guest_gpu/gpu_format.h"
|
||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/guest_gpu/tile.h"
|
#include "graphics/guest_gpu/tile.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
@@ -21,41 +22,22 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
static std::atomic<uint32_t> g_render_color_log_count = 0;
|
static std::atomic<uint32_t> g_render_color_log_count = 0;
|
||||||
|
|
||||||
static void ResolveDccClearInfo(RenderColorInfo& info, vk::Format format, bool has_dcc,
|
static bool DccAlphaOnMsb(const HW::ColorInfo& info) {
|
||||||
uint32_t packed_clear) {
|
switch (info.format) {
|
||||||
// Register-backed DCC clears use the target's packed clear value. Decode one-word guest
|
case Prospero::ChannelLayout::k10_10_10_2:
|
||||||
// formats here; unsupported encodings remain tracked without unsafe materialization.
|
case Prospero::ChannelLayout::k10_10_10_2Float:
|
||||||
info.metadata_clear_supported =
|
case Prospero::ChannelLayout::k5_5_5_1: return true;
|
||||||
has_dcc && DecodePackedColorClear(format, packed_clear, info.color_clear_value);
|
case Prospero::ChannelLayout::k2_10_10_10:
|
||||||
switch (format) {
|
case Prospero::ChannelLayout::k1_5_5_5: return false;
|
||||||
case vk::Format::eR8Unorm:
|
default: break;
|
||||||
case vk::Format::eR8G8Unorm:
|
|
||||||
case vk::Format::eR8G8B8A8Unorm:
|
|
||||||
case vk::Format::eR8G8B8A8Srgb:
|
|
||||||
case vk::Format::eB8G8R8A8Unorm:
|
|
||||||
case vk::Format::eB8G8R8A8Srgb:
|
|
||||||
case vk::Format::eA2B10G10R10UnormPack32:
|
|
||||||
case vk::Format::eA2R10G10B10UnormPack32:
|
|
||||||
case vk::Format::eR5G6B5UnormPack16:
|
|
||||||
case vk::Format::eA1R5G5B5UnormPack16:
|
|
||||||
case vk::Format::eR4G4B4A4UnormPack16:
|
|
||||||
case vk::Format::eR16Unorm:
|
|
||||||
case vk::Format::eR16G16Unorm:
|
|
||||||
case vk::Format::eR16G16B16A16Unorm:
|
|
||||||
case vk::Format::eR16Sfloat:
|
|
||||||
case vk::Format::eR16G16Sfloat:
|
|
||||||
case vk::Format::eR16G16B16A16Sfloat:
|
|
||||||
case vk::Format::eR32Sfloat:
|
|
||||||
case vk::Format::eR32G32Sfloat:
|
|
||||||
case vk::Format::eR32G32B32A32Sfloat:
|
|
||||||
case vk::Format::eB10G11R11UfloatPack32:
|
|
||||||
info.metadata_fixed_clear_supported = has_dcc;
|
|
||||||
break;
|
|
||||||
default: info.metadata_fixed_clear_supported = false; break;
|
|
||||||
}
|
}
|
||||||
if (!info.metadata_clear_supported) {
|
const auto components =
|
||||||
info.color_clear_value = {};
|
Prospero::ResolveRenderTargetFormat(info.format, info.channel_type).components;
|
||||||
|
if (components == 1) {
|
||||||
|
return info.channel_order != Prospero::ChannelOrder::kStandard;
|
||||||
}
|
}
|
||||||
|
return components == 3 || info.channel_order == Prospero::ChannelOrder::kStandard ||
|
||||||
|
info.channel_order == Prospero::ChannelOrder::kAlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
@@ -329,10 +311,14 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
|||||||
desc.info.tile_mode = rt.attrib3.tile_mode;
|
desc.info.tile_mode = rt.attrib3.tile_mode;
|
||||||
const bool has_dcc = rt.info.dcc_compression_enable && rt.dcc_addr.addr != 0;
|
const bool has_dcc = rt.info.dcc_compression_enable && rt.dcc_addr.addr != 0;
|
||||||
if (has_dcc) {
|
if (has_dcc) {
|
||||||
// DCC uses a separate metadata allocation. Carry its address through ImageInfo so
|
TileSizeAlign metadata_size {};
|
||||||
// TextureCache can associate metadata fills observed before target registration.
|
(void)TileGetDccSize(width, height, volume ? depth : view.image_layers, bytes_per_element,
|
||||||
|
levels, rt.attrib3.tile_mode, metadata_size, rt.attrib.num_fragments);
|
||||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||||
desc.info.metadata.range.address = rt.dcc_addr.addr;
|
desc.info.metadata.range = {rt.dcc_addr.addr, metadata_size.size};
|
||||||
|
desc.info.metadata.dcc_clear_word = rt.clear_word0.word0;
|
||||||
|
desc.info.metadata.dcc_clear_register_valid = true;
|
||||||
|
desc.info.metadata.dcc_alpha_msb = DccAlphaOnMsb(rt.info);
|
||||||
}
|
}
|
||||||
for (uint32_t level = 0; level < levels; level++) {
|
for (uint32_t level = 0; level < levels; level++) {
|
||||||
if (volume) {
|
if (volume) {
|
||||||
@@ -378,7 +364,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
|||||||
r.guest_array_layer = view.base_layer;
|
r.guest_array_layer = view.base_layer;
|
||||||
r.image_id = texture_cache.FindImage(r.desc, exact_format);
|
r.image_id = texture_cache.FindImage(r.desc, exact_format);
|
||||||
r.export_mapping = target_format.export_mapping;
|
r.export_mapping = target_format.export_mapping;
|
||||||
ResolveDccClearInfo(r, target_format.format, has_dcc, rt.clear_word0.word0);
|
|
||||||
BindRenderTarget(r.image_id);
|
BindRenderTarget(r.image_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ struct RenderColorInfo {
|
|||||||
uint32_t guest_mip_level = 0;
|
uint32_t guest_mip_level = 0;
|
||||||
uint32_t guest_array_layer = 0;
|
uint32_t guest_array_layer = 0;
|
||||||
Prospero::ColorComponentMapping export_mapping;
|
Prospero::ColorComponentMapping export_mapping;
|
||||||
bool metadata_clear_supported = false;
|
|
||||||
bool metadata_fixed_clear_supported = false;
|
|
||||||
vk::ClearColorValue color_clear_value {};
|
|
||||||
|
|
||||||
[[nodiscard]] vk::Extent2D Extent() const {
|
[[nodiscard]] vk::Extent2D Extent() const {
|
||||||
return {std::max(desc.info.extent.width >> guest_mip_level, 1u),
|
return {std::max(desc.info.extent.width >> guest_mip_level, 1u),
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ struct ImageMetadataInfo {
|
|||||||
GuestRange range;
|
GuestRange range;
|
||||||
ImageMetadataKind kind = ImageMetadataKind::None;
|
ImageMetadataKind kind = ImageMetadataKind::None;
|
||||||
uint32_t control = 0;
|
uint32_t control = 0;
|
||||||
|
uint32_t dcc_clear_word = 0;
|
||||||
VideoOutCompression compression = VideoOutCompression::Uncompressed;
|
VideoOutCompression compression = VideoOutCompression::Uncompressed;
|
||||||
bool stencil_compressed = false;
|
bool stencil_compressed = false;
|
||||||
|
bool dcc_clear_register_valid = false;
|
||||||
|
bool dcc_alpha_msb = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageSubresources {
|
struct ImageSubresources {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <bit>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -758,6 +759,16 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
|||||||
block_bytes != 0 ? block_bytes : Prospero::NumBytesPerElement(format);
|
block_bytes != 0 ? block_bytes : Prospero::NumBytesPerElement(format);
|
||||||
desc.info.samples = samples;
|
desc.info.samples = samples;
|
||||||
desc.info.tile_mode = tile;
|
desc.info.tile_mode = tile;
|
||||||
|
if (!resource.r128 && descriptor.MetaCompress() && tile != Prospero::TileMode::kDepth &&
|
||||||
|
!desc.info.IsDepth()) {
|
||||||
|
TileSizeAlign metadata_size {};
|
||||||
|
(void)TileGetDccSize(width, height, volume ? depth : image_layers,
|
||||||
|
desc.info.bytes_per_block, levels, tile, metadata_size,
|
||||||
|
std::countr_zero(samples));
|
||||||
|
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||||
|
desc.info.metadata.range = {descriptor.MetaAddr() << 8u, metadata_size.size};
|
||||||
|
desc.info.metadata.dcc_alpha_msb = descriptor.DccAlphaPos();
|
||||||
|
}
|
||||||
if (samples > 1) {
|
if (samples > 1) {
|
||||||
desc.info.mip_layout[0] = {0, size.size, pitch, height};
|
desc.info.mip_layout[0] = {0, size.size, pitch, height};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -189,19 +189,15 @@ bool RenderExecutor::TryConsumeComputeImageClear(const ShaderComputeInputInfo& i
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!cache.ClearImageFromBuffer(command, descriptor.Base48(), size, packed_clear)) {
|
if (!cache.ClearImageFromBuffer(command, descriptor.Base48(), size, packed_clear)) {
|
||||||
// Recognized metadata-fill shaders access DCC as an ordinary storage buffer and may run
|
// Track deferred DCC state while the original dispatch writes the metadata allocation.
|
||||||
// before the render target is bound. TryConsumeDccFill either consumes registered state
|
cache.TrackDccFill(descriptor.Base48(), size, packed_clear);
|
||||||
// or retains a PendingDcc fill while allowing the dispatch to run.
|
|
||||||
const bool registered_metadata =
|
|
||||||
cache.TryConsumeDccFill(descriptor.Base48(), size, packed_clear);
|
|
||||||
static std::atomic<uint32_t> logged_metadata_clears {0};
|
static std::atomic<uint32_t> logged_metadata_clears {0};
|
||||||
if (logged_metadata_clears.fetch_add(1, std::memory_order_relaxed) < 32) {
|
if (logged_metadata_clears.fetch_add(1, std::memory_order_relaxed) < 32) {
|
||||||
LOGF("GraphicsRenderDispatchDirect: %s metadata clear shader=0x%016" PRIx64
|
LOGF("GraphicsRenderDispatchDirect: metadata fill shader=0x%016" PRIx64
|
||||||
" addr=0x%016" PRIx64 " size=0x%016" PRIx64 " value=0x%08" PRIx32 "\n",
|
" addr=0x%016" PRIx64 " size=0x%016" PRIx64 " value=0x%08" PRIx32 "\n",
|
||||||
registered_metadata ? "tracked" : "deferred", input.stage.program->shader_hash,
|
input.stage.program->shader_hash, descriptor.Base48(), size, packed_clear);
|
||||||
descriptor.Base48(), size, packed_clear);
|
|
||||||
}
|
}
|
||||||
return registered_metadata;
|
return false;
|
||||||
}
|
}
|
||||||
static std::atomic<uint32_t> logged_clears {0};
|
static std::atomic<uint32_t> logged_clears {0};
|
||||||
if (logged_clears.fetch_add(1, std::memory_order_relaxed) < 32) {
|
if (logged_clears.fetch_add(1, std::memory_order_relaxed) < 32) {
|
||||||
|
|||||||
@@ -215,15 +215,14 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
|||||||
LOGF(
|
LOGF(
|
||||||
"DrawTargetState[%u]: frame=%d %s target=%s addr=0x%010" PRIx64
|
"DrawTargetState[%u]: frame=%d %s target=%s addr=0x%010" PRIx64
|
||||||
" extent=%ux%u prim=%u index_count=%u flags=0x%08" PRIx32 " color_mask=0x%08" PRIx32
|
" extent=%ux%u prim=%u index_count=%u flags=0x%08" PRIx32 " color_mask=0x%08" PRIx32
|
||||||
" clear=%s clear_rgba=(%.3f,%.3f,%.3f,%.3f) cc_mode=%u cc_op=0x%02x"
|
" cc_mode=%u cc_op=0x%02x"
|
||||||
" blend=%s src=%u dst=%u comb=%u ps_tex=%d sampled=%d storage=%d ps_kill=%s target_mode0=%u"
|
" blend=%s src=%u dst=%u comb=%u ps_tex=%d sampled=%d storage=%d ps_kill=%s target_mode0=%u"
|
||||||
" depth_test=%s depth_write=%s depth_func=%u depth_clear=%s viewport=(%.1f,%.1f %.1fx%.1f) "
|
" depth_test=%s depth_write=%s depth_func=%u depth_clear=%s viewport=(%.1f,%.1f %.1fx%.1f) "
|
||||||
"scissor=(%d,%d)-(%d,%d)\n",
|
"scissor=(%d,%d)-(%d,%d)\n",
|
||||||
log_id, buffer.GetContext().GetGpu().GetFrameNum(), draw_name, RenderColorTypeName(color),
|
log_id, buffer.GetContext().GetGpu().GetFrameNum(), draw_name, RenderColorTypeName(color),
|
||||||
color.desc.info.data.address, extent.width, extent.height,
|
color.desc.info.data.address, extent.width, extent.height,
|
||||||
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags, ctx.GetRenderTargetMask(),
|
static_cast<uint32_t>(ucfg.GetPrimType()), index_count, flags, ctx.GetRenderTargetMask(),
|
||||||
"false", color.color_clear_value.float32[0], color.color_clear_value.float32[1],
|
cc.mode, cc.op,
|
||||||
color.color_clear_value.float32[2], color.color_clear_value.float32[3], cc.mode, cc.op,
|
|
||||||
bc.enable ? "true" : "false", bc.color_srcblend, bc.color_destblend, bc.color_comb_fcn,
|
bc.enable ? "true" : "false", bc.color_srcblend, bc.color_destblend, bc.color_comb_fcn,
|
||||||
static_cast<int>(ps_resources.images.size()), static_cast<int>(sampled_images),
|
static_cast<int>(ps_resources.images.size()), static_cast<int>(sampled_images),
|
||||||
static_cast<int>(ps_resources.images.size() - sampled_images),
|
static_cast<int>(ps_resources.images.size() - sampled_images),
|
||||||
@@ -460,70 +459,6 @@ struct DrawCallInfo {
|
|||||||
uint32_t first_instance = 0;
|
uint32_t first_instance = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool ResolveDccAttachmentClear(TextureCache& cache, const RenderColorInfo& target,
|
|
||||||
const ImageViewInfo& view, vk::ClearColorValue& clear_value) {
|
|
||||||
if (target.desc.info.metadata.kind != ImageMetadataKind::Dcc) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A DCC fast clear may update metadata only and leave the color allocation stale. Vulkan has
|
|
||||||
// no guest DCC state, so materialize the deferred value when the surface is next bound.
|
|
||||||
const auto metadata_address = target.desc.info.metadata.range.address;
|
|
||||||
uint32_t metadata_value = 0xffffffffu;
|
|
||||||
if (!cache.IsMetaCleared(metadata_address, view.base_layer, &metadata_value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate recognized constant and register-backed DCC states into a host clear value.
|
|
||||||
clear_value = {};
|
|
||||||
switch (static_cast<uint8_t>(metadata_value)) {
|
|
||||||
case 0x00: break;
|
|
||||||
case 0x20:
|
|
||||||
if (!target.metadata_clear_supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
clear_value = target.color_clear_value;
|
|
||||||
break;
|
|
||||||
case 0x40:
|
|
||||||
if (!target.metadata_fixed_clear_supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
clear_value.float32[3] = 1.0f;
|
|
||||||
break;
|
|
||||||
case 0x80:
|
|
||||||
if (!target.metadata_fixed_clear_supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
clear_value.float32[0] = 1.0f;
|
|
||||||
clear_value.float32[1] = 1.0f;
|
|
||||||
clear_value.float32[2] = 1.0f;
|
|
||||||
break;
|
|
||||||
case 0xc0:
|
|
||||||
if (!target.metadata_fixed_clear_supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
clear_value.float32[0] = 1.0f;
|
|
||||||
clear_value.float32[1] = 1.0f;
|
|
||||||
clear_value.float32[2] = 1.0f;
|
|
||||||
clear_value.float32[3] = 1.0f;
|
|
||||||
break;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t layer = 1; layer < view.layer_count; layer++) {
|
|
||||||
if (!cache.IsMetaCleared(metadata_address, view.base_layer + layer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Consume only after every layer in this Vulkan view can be materialized together.
|
|
||||||
for (uint32_t layer = 0; layer < view.layer_count; layer++) {
|
|
||||||
if (!cache.TouchMeta(metadata_address, view.base_layer + layer, false)) {
|
|
||||||
EXIT("failed to consume DCC clear state\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||||
uint32_t color_count, RenderDepthInfo& depth) {
|
uint32_t color_count, RenderDepthInfo& depth) {
|
||||||
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||||
@@ -581,14 +516,6 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
|||||||
auto& attachment = state.color_attachments[i];
|
auto& attachment = state.color_attachments[i];
|
||||||
attachment.image_view = image_view;
|
attachment.image_view = image_view;
|
||||||
attachment.image_layout = layout;
|
attachment.image_layout = layout;
|
||||||
attachment.clear_value = target.color_clear_value.uint32;
|
|
||||||
vk::ClearColorValue metadata_clear_value {};
|
|
||||||
const bool metadata_clear =
|
|
||||||
ResolveDccAttachmentClear(cache, target, view, metadata_clear_value);
|
|
||||||
if (metadata_clear) {
|
|
||||||
attachment.clear_value = metadata_clear_value.uint32;
|
|
||||||
}
|
|
||||||
attachment.is_clear = metadata_clear;
|
|
||||||
}
|
}
|
||||||
if (depth.image_id) {
|
if (depth.image_id) {
|
||||||
const auto owner = cache.m_slot_images.try_get(depth.image_id);
|
const auto owner = cache.m_slot_images.try_get(depth.image_id);
|
||||||
|
|||||||
@@ -5796,9 +5796,9 @@ public:
|
|||||||
texture_cache.FindImage(metadata_depth_a);
|
texture_cache.FindImage(metadata_depth_a);
|
||||||
const auto metadata_depth_b_id =
|
const auto metadata_depth_b_id =
|
||||||
texture_cache.FindImage(metadata_depth_b);
|
texture_cache.FindImage(metadata_depth_b);
|
||||||
|
texture_cache.TrackDccFill(base + metadata_a, 0x80, 0);
|
||||||
Require(
|
Require(
|
||||||
name, "pending DCC/HTile separation",
|
name, "pending DCC/HTile separation",
|
||||||
!texture_cache.TryConsumeDccFill(base + metadata_a, 0x80, 0) &&
|
|
||||||
!texture_cache.IsMeta(base + metadata_a),
|
!texture_cache.IsMeta(base + metadata_a),
|
||||||
"an unclassified PS5 DCC fill was exposed as registered metadata");
|
"an unclassified PS5 DCC fill was exposed as registered metadata");
|
||||||
const auto metadata_depth_a_view =
|
const auto metadata_depth_a_view =
|
||||||
@@ -5832,11 +5832,11 @@ public:
|
|||||||
texture_cache.IsMeta(base + metadata_b) &&
|
texture_cache.IsMeta(base + metadata_b) &&
|
||||||
texture_cache.IsMetaCleared(base + metadata_b, 0),
|
texture_cache.IsMetaCleared(base + metadata_b, 0),
|
||||||
"image discovery retired aliased metadata or its live depth image");
|
"image discovery retired aliased metadata or its live depth image");
|
||||||
|
const bool touched_metadata = texture_cache.TouchMeta(base + metadata_a, 0, false);
|
||||||
|
texture_cache.TrackDccFill(base + metadata_a, 0x80, 0);
|
||||||
Require(
|
Require(
|
||||||
name, "registered DCC/HTile separation",
|
name, "registered DCC/HTile separation",
|
||||||
texture_cache.TouchMeta(base + metadata_a, 0, false) &&
|
touched_metadata && !texture_cache.IsMetaCleared(base + metadata_a, 0),
|
||||||
!texture_cache.TryConsumeDccFill(base + metadata_a, 0x80, 0) &&
|
|
||||||
!texture_cache.IsMetaCleared(base + metadata_a, 0),
|
|
||||||
"a PS5 DCC fill changed registered HTile state");
|
"a PS5 DCC fill changed registered HTile state");
|
||||||
Require(
|
Require(
|
||||||
name, "metadata first-touch state",
|
name, "metadata first-touch state",
|
||||||
@@ -7649,7 +7649,9 @@ public:
|
|||||||
void CheckRenderExecutorColorVolumeDiscovery() {
|
void CheckRenderExecutorColorVolumeDiscovery() {
|
||||||
constexpr const char *name = "RenderExecutorColorVolumeDiscovery";
|
constexpr const char *name = "RenderExecutorColorVolumeDiscovery";
|
||||||
constexpr uintptr_t base = 0x0000000203e00000ull;
|
constexpr uintptr_t base = 0x0000000203e00000ull;
|
||||||
constexpr uint64_t allocation_size = 0x200000;
|
constexpr uint64_t color_size = 0x200000;
|
||||||
|
constexpr uint64_t metadata_size = 0x20000;
|
||||||
|
constexpr uint64_t allocation_size = color_size + metadata_size;
|
||||||
constexpr uint64_t allocation_alignment = 0x10000;
|
constexpr uint64_t allocation_alignment = 0x10000;
|
||||||
EnsureRuntimeContext();
|
EnsureRuntimeContext();
|
||||||
|
|
||||||
@@ -7689,7 +7691,7 @@ public:
|
|||||||
.tile_mode = Prospero::TileMode::kRenderTarget,
|
.tile_mode = Prospero::TileMode::kRenderTarget,
|
||||||
.dimension = 2,
|
.dimension = 2,
|
||||||
.metadata_pipe_aligned = true});
|
.metadata_pipe_aligned = true});
|
||||||
constexpr uint64_t dcc_address = base + allocation_size - 0x1000;
|
constexpr uint64_t dcc_address = base + color_size;
|
||||||
registers.SetColorDccAddr(0, {.addr = dcc_address});
|
registers.SetColorDccAddr(0, {.addr = dcc_address});
|
||||||
registers.SetColorClearWord0(0, {.word0 = 0});
|
registers.SetColorClearWord0(0, {.word0 = 0});
|
||||||
registers.SetColorClearWord1(0, {.word1 = 0});
|
registers.SetColorClearWord1(0, {.word1 = 0});
|
||||||
@@ -7700,8 +7702,8 @@ public:
|
|||||||
auto &texture_cache = resources.GetTextureCache();
|
auto &texture_cache = resources.GetTextureCache();
|
||||||
auto &executor = context.GetRenderExecutor();
|
auto &executor = context.GetRenderExecutor();
|
||||||
resources.MapMemory(base, allocation_size);
|
resources.MapMemory(base, allocation_size);
|
||||||
|
texture_cache.TrackDccFill(dcc_address, metadata_size, 0);
|
||||||
Require(name, "deferred DCC clear",
|
Require(name, "deferred DCC clear",
|
||||||
!texture_cache.TryConsumeDccFill(dcc_address, 0x1000, 0) &&
|
|
||||||
!texture_cache.IsMeta(dcc_address),
|
!texture_cache.IsMeta(dcc_address),
|
||||||
"a pre-registration DCC clear was not deferred");
|
"a pre-registration DCC clear was not deferred");
|
||||||
|
|
||||||
@@ -7719,11 +7721,10 @@ public:
|
|||||||
color.desc.info.extent == vk::Extent3D{32, 32, 32} &&
|
color.desc.info.extent == vk::Extent3D{32, 32, 32} &&
|
||||||
color.desc.info.resources == ImageSubresources{1, 1} &&
|
color.desc.info.resources == ImageSubresources{1, 1} &&
|
||||||
color.desc.info.pitch == 128 &&
|
color.desc.info.pitch == 128 &&
|
||||||
color.desc.info.data.size == allocation_size &&
|
color.desc.info.data.size == color_size &&
|
||||||
color.desc.info.mip_layout[0].size == 0x10000 &&
|
color.desc.info.mip_layout[0].size == 0x10000 &&
|
||||||
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
||||||
color.desc.info.metadata.range.address == dcc_address &&
|
color.desc.info.metadata.range.address == dcc_address &&
|
||||||
color.metadata_clear_supported &&
|
|
||||||
texture_cache.IsMetaCleared(dcc_address, 7, &dcc_clear_value) &&
|
texture_cache.IsMetaCleared(dcc_address, 7, &dcc_clear_value) &&
|
||||||
dcc_clear_value == 0 && !texture_cache.ClearMeta(dcc_address) &&
|
dcc_clear_value == 0 && !texture_cache.ClearMeta(dcc_address) &&
|
||||||
color.desc.view_info.type == vk::ImageViewType::e2D &&
|
color.desc.view_info.type == vk::ImageViewType::e2D &&
|
||||||
@@ -7753,23 +7754,17 @@ public:
|
|||||||
sliced_color.desc.view_info.base_layer == 7 &&
|
sliced_color.desc.view_info.base_layer == 7 &&
|
||||||
sliced_rendering.num_color_attachments == 1 &&
|
sliced_rendering.num_color_attachments == 1 &&
|
||||||
sliced_rendering.num_layers == 1 &&
|
sliced_rendering.num_layers == 1 &&
|
||||||
sliced_rendering.color_attachments[0].is_clear &&
|
|
||||||
sliced_rendering.color_attachments[0].clear_value ==
|
|
||||||
vk::ClearColorValue{}.uint32 &&
|
|
||||||
!texture_cache.IsMetaCleared(dcc_address, 7),
|
!texture_cache.IsMetaCleared(dcc_address, 7),
|
||||||
"a nonzero 3D attachment slice was treated as a Vulkan array layer");
|
"a nonzero 3D attachment slice was treated as a Vulkan array layer");
|
||||||
dcc_clear_value = 0;
|
Require(name, "3D slice clear and preservation",
|
||||||
Require(
|
ReadCachedTexel(name, context, color.image_id, {0, 0, 7}) ==
|
||||||
name, "registered DCC code state",
|
std::vector<u32>{0} &&
|
||||||
texture_cache.TryConsumeDccFill(dcc_address, 0x1000, 0xffffffffu) &&
|
ReadCachedTexel(name, context, color.image_id, {0, 0, 31}) ==
|
||||||
!texture_cache.IsMetaCleared(dcc_address, 7, &dcc_clear_value) &&
|
std::vector<u32>{0x5a5a5a5a},
|
||||||
dcc_clear_value == 0xffffffffu &&
|
"clearing one volume slice changed untouched slice contents");
|
||||||
texture_cache.TryConsumeDccFill(dcc_address, 0x1000, 0) &&
|
|
||||||
texture_cache.IsMetaCleared(dcc_address, 7),
|
|
||||||
"DCC fill codes were not classified as clear or uncompressed");
|
|
||||||
|
|
||||||
auto storage_desc = color.desc;
|
auto storage_desc = color.desc;
|
||||||
storage_desc.type = BindingType::Storage;
|
storage_desc.type = BindingType::Storage;
|
||||||
|
storage_desc.info.metadata = {};
|
||||||
storage_desc.info.guest_format = Prospero::BufferFormat::k10_10_10_2UNorm;
|
storage_desc.info.guest_format = Prospero::BufferFormat::k10_10_10_2UNorm;
|
||||||
storage_desc.view_info.type = vk::ImageViewType::e3D;
|
storage_desc.view_info.type = vk::ImageViewType::e3D;
|
||||||
storage_desc.view_info.usage = vk::ImageUsageFlagBits::eStorage;
|
storage_desc.view_info.usage = vk::ImageUsageFlagBits::eStorage;
|
||||||
@@ -7789,7 +7784,7 @@ public:
|
|||||||
TextureCacheTestAccess::TryDownload(texture_cache, storage_id),
|
TextureCacheTestAccess::TryDownload(texture_cache, storage_id),
|
||||||
"the 3D render target could not be queued for guest-layout readback");
|
"the 3D render target could not be queued for guest-layout readback");
|
||||||
auto mirror = resources.GetBufferCache().ObtainBuffer(
|
auto mirror = resources.GetBufferCache().ObtainBuffer(
|
||||||
base, allocation_size, false, true);
|
base, color_size, false, true);
|
||||||
Require(name, "volume mirror", mirror.first != nullptr,
|
Require(name, "volume mirror", mirror.first != nullptr,
|
||||||
"the 3D render-target readback has no BufferCache owner");
|
"the 3D render-target readback has no BufferCache owner");
|
||||||
auto slice_probe =
|
auto slice_probe =
|
||||||
@@ -7816,6 +7811,17 @@ public:
|
|||||||
"render-target upload/readback lost the final Z slice");
|
"render-target upload/readback lost the final Z slice");
|
||||||
DestroyBuffer(&slice_probe);
|
DestroyBuffer(&slice_probe);
|
||||||
|
|
||||||
|
dcc_clear_value = 0;
|
||||||
|
texture_cache.TrackDccFill(dcc_address, metadata_size, 0xffffffffu);
|
||||||
|
const bool uncompressed =
|
||||||
|
!texture_cache.IsMetaCleared(dcc_address, 7, &dcc_clear_value) &&
|
||||||
|
dcc_clear_value == 0xffffffffu;
|
||||||
|
texture_cache.TrackDccFill(dcc_address, metadata_size, 0);
|
||||||
|
Require(
|
||||||
|
name, "registered DCC code state",
|
||||||
|
uncompressed && texture_cache.IsMetaCleared(dcc_address, 7),
|
||||||
|
"DCC fill codes were not classified as clear or uncompressed");
|
||||||
|
|
||||||
for (const uint32_t base_slice : {0u, 7u}) {
|
for (const uint32_t base_slice : {0u, 7u}) {
|
||||||
RenderExecutorTestAccess::ResetBindings(executor);
|
RenderExecutorTestAccess::ResetBindings(executor);
|
||||||
registers.SetColorView(0, {.base_array_slice_index = base_slice,
|
registers.SetColorView(0, {.base_array_slice_index = base_slice,
|
||||||
@@ -7829,7 +7835,7 @@ public:
|
|||||||
Require(name, "3D export bound exceeds depth",
|
Require(name, "3D export bound exceeds depth",
|
||||||
volume_color.image_id == color.image_id &&
|
volume_color.image_id == color.image_id &&
|
||||||
volume_color.desc.info.extent == vk::Extent3D{32, 32, 32} &&
|
volume_color.desc.info.extent == vk::Extent3D{32, 32, 32} &&
|
||||||
volume_color.desc.info.data.size == allocation_size &&
|
volume_color.desc.info.data.size == color_size &&
|
||||||
volume_color.desc.view_info.type == vk::ImageViewType::e2DArray &&
|
volume_color.desc.view_info.type == vk::ImageViewType::e2DArray &&
|
||||||
volume_color.desc.view_info.base_layer == base_slice &&
|
volume_color.desc.view_info.base_layer == base_slice &&
|
||||||
volume_color.desc.view_info.layer_count == 32 - base_slice &&
|
volume_color.desc.view_info.layer_count == 32 - base_slice &&
|
||||||
@@ -7855,6 +7861,40 @@ public:
|
|||||||
std::printf("[gpu] %-32s ok\n", name);
|
std::printf("[gpu] %-32s ok\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<u32> ReadCachedTexel(const char *name, RenderContext &context,
|
||||||
|
ImageId id, vk::Offset3D offset = {}) {
|
||||||
|
auto &scheduler = context.GetCommandScheduler();
|
||||||
|
auto &image = context.GetTextureCache().GetImage(id);
|
||||||
|
auto probe = CreateHostBuffer(name, image.info.bytes_per_block,
|
||||||
|
vk::BufferUsageFlagBits::eTransferDst, {});
|
||||||
|
scheduler.Current().EndRendering();
|
||||||
|
image.Transit(vk::ImageLayout::eTransferSrcOptimal,
|
||||||
|
vk::AccessFlagBits2::eTransferRead, {},
|
||||||
|
scheduler.Current().Handle());
|
||||||
|
vk::BufferImageCopy copy{};
|
||||||
|
copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
|
||||||
|
copy.imageOffset = offset;
|
||||||
|
copy.imageExtent = vk::Extent3D{1, 1, 1};
|
||||||
|
scheduler.Current().Handle().copyImageToBuffer(
|
||||||
|
image.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||||
|
probe.buffer, 1, ©);
|
||||||
|
vk::BufferMemoryBarrier barrier{};
|
||||||
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
|
barrier.dstAccessMask = vk::AccessFlagBits::eHostRead;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.buffer = probe.buffer;
|
||||||
|
barrier.size = probe.size;
|
||||||
|
scheduler.Current().Handle().pipelineBarrier(
|
||||||
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
|
vk::PipelineStageFlagBits::eHost, {}, 0, nullptr, 1, &barrier, 0,
|
||||||
|
nullptr);
|
||||||
|
scheduler.Finish();
|
||||||
|
auto result = ReadBuffer(name, probe, image.info.bytes_per_block / 4);
|
||||||
|
DestroyBuffer(&probe);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckRenderExecutorDccFixedClearFloat() {
|
void CheckRenderExecutorDccFixedClearFloat() {
|
||||||
constexpr const char *name = "RenderExecutorDccFixedClearFloat";
|
constexpr const char *name = "RenderExecutorDccFixedClearFloat";
|
||||||
constexpr uintptr_t base = 0x0000000204100000ull;
|
constexpr uintptr_t base = 0x0000000204100000ull;
|
||||||
@@ -7863,11 +7903,11 @@ public:
|
|||||||
constexpr uint64_t dcc_address = base + 0x100000;
|
constexpr uint64_t dcc_address = base + 0x100000;
|
||||||
struct FillCase {
|
struct FillCase {
|
||||||
uint32_t fill;
|
uint32_t fill;
|
||||||
std::array<uint32_t, 4> clear;
|
std::array<uint32_t, 2> texel;
|
||||||
};
|
};
|
||||||
constexpr std::array cases{
|
constexpr std::array cases{
|
||||||
FillCase{0x40404040u, {0, 0, 0, 0x3f800000u}},
|
FillCase{0x40404040u, {0, 0x3c000000u}},
|
||||||
FillCase{0x80808080u, {0x3f800000u, 0x3f800000u, 0x3f800000u, 0}},
|
FillCase{0x80808080u, {0x3c003c00u, 0x00003c00u}},
|
||||||
};
|
};
|
||||||
EnsureRuntimeContext();
|
EnsureRuntimeContext();
|
||||||
|
|
||||||
@@ -7913,9 +7953,9 @@ public:
|
|||||||
auto &texture_cache = resources.GetTextureCache();
|
auto &texture_cache = resources.GetTextureCache();
|
||||||
auto &executor = context.GetRenderExecutor();
|
auto &executor = context.GetRenderExecutor();
|
||||||
resources.MapMemory(base, allocation_size);
|
resources.MapMemory(base, allocation_size);
|
||||||
|
texture_cache.TrackDccFill(dcc_address, 0x1000, fill_case.fill);
|
||||||
Require(name, "deferred fixed-code fill",
|
Require(name, "deferred fixed-code fill",
|
||||||
!texture_cache.TryConsumeDccFill(dcc_address, 0x1000,
|
!texture_cache.IsMeta(dcc_address),
|
||||||
fill_case.fill),
|
|
||||||
"a pre-registration DCC fill was not deferred");
|
"a pre-registration DCC fill was not deferred");
|
||||||
|
|
||||||
RenderColorInfo color{};
|
RenderColorInfo color{};
|
||||||
@@ -7928,14 +7968,14 @@ public:
|
|||||||
color.image_id &&
|
color.image_id &&
|
||||||
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
||||||
color.desc.info.metadata.range.address == dcc_address &&
|
color.desc.info.metadata.range.address == dcc_address &&
|
||||||
color.metadata_fixed_clear_supported &&
|
|
||||||
rendering.num_color_attachments == 1 &&
|
rendering.num_color_attachments == 1 &&
|
||||||
rendering.color_attachments[0].is_clear &&
|
|
||||||
rendering.color_attachments[0].clear_value ==
|
|
||||||
fill_case.clear &&
|
|
||||||
!texture_cache.IsMetaCleared(dcc_address, 0),
|
!texture_cache.IsMetaCleared(dcc_address, 0),
|
||||||
"a DCC fixed clear code was not materialised on an RGBA16F "
|
"a DCC fixed clear code was not materialised on an RGBA16F "
|
||||||
"target");
|
"target");
|
||||||
|
Require(name, "GPU float clear value",
|
||||||
|
ReadCachedTexel(name, context, color.image_id) ==
|
||||||
|
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
|
||||||
|
"RGBA16F native image did not receive the fixed clear value");
|
||||||
|
|
||||||
RenderExecutorTestAccess::ResetBindings(executor);
|
RenderExecutorTestAccess::ResetBindings(executor);
|
||||||
resources.UnmapMemory(base, allocation_size);
|
resources.UnmapMemory(base, allocation_size);
|
||||||
@@ -7952,6 +7992,100 @@ public:
|
|||||||
std::printf("[gpu] %-32s ok\n", name);
|
std::printf("[gpu] %-32s ok\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckSampledDccClear() {
|
||||||
|
constexpr const char *name = "SampledDccClear";
|
||||||
|
constexpr uintptr_t base = 0x0000000205000000ull;
|
||||||
|
constexpr uint64_t color_size = 0x3fc0000;
|
||||||
|
constexpr uint64_t metadata_size = 0x48000;
|
||||||
|
constexpr uint64_t dcc_address = base + color_size;
|
||||||
|
constexpr uint64_t allocation_size = 0x4020000;
|
||||||
|
constexpr uint64_t alignment = 0x10000;
|
||||||
|
EnsureRuntimeContext();
|
||||||
|
int64_t direct_offset = -1;
|
||||||
|
Require(name, "direct allocation",
|
||||||
|
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||||
|
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(),
|
||||||
|
allocation_size, alignment, 0, &direct_offset) == 0,
|
||||||
|
"captured scene allocation failed");
|
||||||
|
void *mapped = reinterpret_cast<void *>(base);
|
||||||
|
Require(name, "direct mapping",
|
||||||
|
Libs::LibKernel::Memory::KernelMapDirectMemory(
|
||||||
|
&mapped, allocation_size, 0x3, 0x10, direct_offset, alignment) == 0 &&
|
||||||
|
mapped == reinterpret_cast<void *>(base),
|
||||||
|
"captured scene mapping failed");
|
||||||
|
for (const auto initial_size : {metadata_size - 0x1000, metadata_size}) {
|
||||||
|
std::memset(mapped, 0x5a, color_size);
|
||||||
|
std::memset(reinterpret_cast<void *>(dcc_address), 0x40, metadata_size);
|
||||||
|
RenderContext context(m_runtime_context);
|
||||||
|
auto &scheduler = context.GetCommandScheduler();
|
||||||
|
HW::Context registers{};
|
||||||
|
HW::UserConfig user_config{};
|
||||||
|
HW::Shader shaders{};
|
||||||
|
scheduler.Begin(registers, user_config, shaders);
|
||||||
|
auto &resources = context.GetGpuResources();
|
||||||
|
auto &cache = resources.GetTextureCache();
|
||||||
|
auto &executor = context.GetRenderExecutor();
|
||||||
|
resources.MapMemory(base, allocation_size);
|
||||||
|
cache.TrackDccFill(dcc_address, initial_size, 0x40404040u);
|
||||||
|
Require(name, "deferred metadata fill",
|
||||||
|
!cache.IsMeta(dcc_address),
|
||||||
|
"unknown metadata was consumed before descriptor discovery");
|
||||||
|
|
||||||
|
// PPSA19244's first sampled scene descriptor, with relocated allocations.
|
||||||
|
ShaderTextureResource descriptor{{static_cast<uint32_t>(base >> 8u),
|
||||||
|
0xc4700000u, 0x021bc3bfu, 0x91b00facu, 0, 0x00700000u,
|
||||||
|
0x006b0000u | (static_cast<uint32_t>(dcc_address >> 8u) << 24u),
|
||||||
|
static_cast<uint32_t>(dcc_address >> 16u)}};
|
||||||
|
ShaderRecompiler::IR::DescriptorValue value{};
|
||||||
|
value.dword_count = 8;
|
||||||
|
std::copy_n(descriptor.fields, 8, value.dwords.begin());
|
||||||
|
ShaderRecompiler::IR::ImageResource resource{};
|
||||||
|
resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled;
|
||||||
|
resource.numeric_class = Prospero::TextureNumericClass::Float;
|
||||||
|
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||||
|
resource.read = true;
|
||||||
|
auto binding = RenderExecutorTestAccess::ResolveTexture(executor, resource, value);
|
||||||
|
Require(name, "sampled metadata discovery",
|
||||||
|
binding.desc.info.extent == vk::Extent3D{3840, 2160, 1} &&
|
||||||
|
binding.desc.info.data.size == color_size &&
|
||||||
|
binding.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
||||||
|
binding.desc.info.metadata.range.address == dcc_address &&
|
||||||
|
binding.desc.info.metadata.range.size == metadata_size,
|
||||||
|
"captured sampled descriptor lost its DCC allocation");
|
||||||
|
Require(name, "sampled view", cache.FindTexture(binding.image_id, binding.desc) != nullptr,
|
||||||
|
"captured scene did not produce a sampled view");
|
||||||
|
if (initial_size != metadata_size) {
|
||||||
|
Require(name, "partial metadata coverage",
|
||||||
|
ReadCachedTexel(name, context, binding.image_id) ==
|
||||||
|
std::vector<u32>{0x5a5a5a5au, 0x5a5a5a5au},
|
||||||
|
"an incomplete metadata fill cleared the whole scene");
|
||||||
|
cache.TrackDccFill(dcc_address, metadata_size, 0x40404040u);
|
||||||
|
(void)cache.FindTexture(binding.image_id, binding.desc);
|
||||||
|
}
|
||||||
|
Require(name, "opaque black before first sample",
|
||||||
|
ReadCachedTexel(name, context, binding.image_id, {3839, 2159, 0}) ==
|
||||||
|
std::vector<u32>{0, 0x3c000000u} &&
|
||||||
|
!cache.IsMetaCleared(dcc_address, 0),
|
||||||
|
"sampling read stale color bytes instead of the DCC opaque black clear");
|
||||||
|
cache.TrackDccFill(dcc_address, metadata_size, 0x80808080u);
|
||||||
|
(void)cache.FindTexture(binding.image_id, binding.desc);
|
||||||
|
Require(name, "later sampled clear",
|
||||||
|
ReadCachedTexel(name, context, binding.image_id) ==
|
||||||
|
std::vector<u32>{0x3c003c00u, 0x00003c00u},
|
||||||
|
"an existing sampled image retained the previous DCC clear");
|
||||||
|
resources.UnmapMemory(base, allocation_size);
|
||||||
|
scheduler.Finish();
|
||||||
|
}
|
||||||
|
Require(name, "unmap",
|
||||||
|
Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0,
|
||||||
|
"captured scene mapping release failed");
|
||||||
|
Require(name, "release",
|
||||||
|
Libs::LibKernel::Memory::KernelReleaseDirectMemory(
|
||||||
|
direct_offset, allocation_size) == 0,
|
||||||
|
"captured scene allocation release failed");
|
||||||
|
std::printf("[gpu] %-32s ok\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckComparisonDepthTexture() {
|
void CheckComparisonDepthTexture() {
|
||||||
constexpr const char *name = "ComparisonDepthTexture";
|
constexpr const char *name = "ComparisonDepthTexture";
|
||||||
constexpr uintptr_t base = 0x0000000204200000ull;
|
constexpr uintptr_t base = 0x0000000204200000ull;
|
||||||
@@ -27472,6 +27606,7 @@ int main(int argc, char **argv) {
|
|||||||
vulkan.CheckRenderExecutorColor1DDiscovery();
|
vulkan.CheckRenderExecutorColor1DDiscovery();
|
||||||
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
||||||
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
||||||
|
vulkan.CheckSampledDccClear();
|
||||||
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
||||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||||
vulkan.CheckUnifiedTextureCacheFlow();
|
vulkan.CheckUnifiedTextureCacheFlow();
|
||||||
@@ -27487,6 +27622,9 @@ int main(int argc, char **argv) {
|
|||||||
CheckDynamicRenderingState();
|
CheckDynamicRenderingState();
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
vulkan.CheckComputeMetaClearClassification();
|
vulkan.CheckComputeMetaClearClassification();
|
||||||
|
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
||||||
|
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
||||||
|
vulkan.CheckSampledDccClear();
|
||||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -27664,6 +27802,7 @@ int main(int argc, char **argv) {
|
|||||||
vulkan.CheckRenderExecutorColor1DDiscovery();
|
vulkan.CheckRenderExecutorColor1DDiscovery();
|
||||||
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
||||||
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
||||||
|
vulkan.CheckSampledDccClear();
|
||||||
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
||||||
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
||||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||||
|
|||||||
Reference in New Issue
Block a user