graphics: execute DB_RENDER_OVERRIDE depth and stencil copies

This commit is contained in:
nmzik
2026-09-09 15:19:37 +04:00
parent 57844a07e2
commit 0b4e78c0b2
6 changed files with 520 additions and 108 deletions
@@ -315,8 +315,17 @@ static void HwCtxSetDepthBoundsRegister(CommandProcessor& cp, uint32_t cmd_offse
}
}
static void HwCtxIgnoreDepthMetadataRegister([[maybe_unused]] uint32_t cmd_offset,
[[maybe_unused]] uint32_t value) {}
static void HwCtxSetDepthMetadataRegister(CommandProcessor& cp, uint32_t cmd_offset,
uint32_t value) {
if (cmd_offset == Pm4::DB_RENDER_OVERRIDE) {
HW::DepthRenderOverride control;
control.force_z_valid = (value & 0x20000000u) != 0;
control.force_z_dirty = (value & 0x08000000u) != 0;
control.force_stencil_valid = (value & 0x40000000u) != 0;
control.force_stencil_dirty = (value & 0x10000000u) != 0;
cp.GetCtx().SetDepthRenderOverride(control);
}
}
static void HwCtxIgnoreFovWindow([[maybe_unused]] uint32_t value) {}
@@ -389,7 +398,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetDepthMetadataRegisters) {
auto num_values = KYTY_PM4_LEN(cmd_id) - 2u;
for (uint32_t i = 0; i < num_values; i++) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset + i, buffer[i]);
HwCtxSetDepthMetadataRegister(cp, cmd_offset + i, buffer[i]);
}
return num_values;
@@ -3003,28 +3012,28 @@ void GraphicsInitJmpTablesCxIndirect() {
HwCtxIgnoreCbDccControl(value);
};
g_hw_ctx_indirect_func[Pm4::DB_COUNT_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
for (auto cmd_offset = Pm4::DB_SRESULTS_COMPARE_STATE0;
cmd_offset <= Pm4::DB_SRESULTS_COMPARE_STATE1; cmd_offset++) {
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
}
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE2] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
g_hw_ctx_indirect_func[Pm4::DB_DFSM_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
g_hw_ctx_indirect_func[Pm4::DB_RMI_L2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
g_hw_ctx_indirect_func[Pm4::CB_RMI_GL2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
};
g_hw_ctx_indirect_func[Pm4::TA_BC_BASE_ADDR] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
HwCtxIgnoreBorderColorTableAddr(cmd_offset, value);
+23 -9
View File
@@ -202,6 +202,13 @@ struct RenderControl {
uint8_t copy_sample = 0;
};
struct DepthRenderOverride {
bool force_z_valid = false;
bool force_z_dirty = false;
bool force_stencil_valid = false;
bool force_stencil_dirty = false;
};
struct GdsOaCounter {
uint32_t counter = 0;
uint32_t address = 0;
@@ -809,6 +816,12 @@ public:
void SetClipControl(const ClipControl& control) { m_clip_control = control; }
[[nodiscard]] const RenderControl& GetRenderControl() const { return m_render_control; }
void SetRenderControl(const RenderControl& control) { m_render_control = control; }
[[nodiscard]] const DepthRenderOverride& GetDepthRenderOverride() const {
return m_depth_render_override;
}
void SetDepthRenderOverride(const DepthRenderOverride& control) {
m_depth_render_override = control;
}
[[nodiscard]] const DepthControl& GetDepthControl() const { return m_depth_control; }
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
@@ -904,15 +917,16 @@ private:
uint32_t m_shader_stages = 0;
DepthRenderTarget m_depth_render_target;
RenderControl m_render_control;
DepthControl m_depth_control;
StencilControl m_stencil_control;
StencilMask m_stencil_mask;
float m_depth_clear_value = 0.0f;
float m_depth_bounds_min = 0.0f;
float m_depth_bounds_max = 1.0f;
uint8_t m_stencil_clear_value = 0;
DepthRenderTarget m_depth_render_target;
RenderControl m_render_control;
DepthRenderOverride m_depth_render_override;
DepthControl m_depth_control;
StencilControl m_stencil_control;
StencilMask m_stencil_mask;
float m_depth_clear_value = 0.0f;
float m_depth_bounds_min = 0.0f;
float m_depth_bounds_max = 1.0f;
uint8_t m_stencil_clear_value = 0;
ModeControl m_mode_control;
PolyOffset m_poly_offset;
@@ -10,6 +10,7 @@
#include "graphics/guest_gpu/hardwareContext.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
@@ -96,51 +97,17 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
return vk::Format::eUndefined;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
KYTY_PROFILER_FUNCTION();
const auto& hw = buffer.GetRegisters();
const auto& z = hw.GetDepthRenderTarget();
const auto& rc = hw.GetRenderControl();
const auto& dc = hw.GetDepthControl();
const auto& sc = hw.GetStencilControl();
const auto& sm = hw.GetStencilMask();
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
rc.depth_clear_enable || rc.copy_depth_to_color;
const bool stencil_active =
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
if (!depth_active && !stencil_active) {
return;
}
static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
const HW::DepthRenderTarget& z,
bool write_buffer = false) {
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
const auto depth_address = write_buffer ? z.z_write_base_addr : z.z_read_base_addr;
const auto stencil_address =
write_buffer ? z.stencil_write_base_addr : z.stencil_read_base_addr;
if (!z.z_info.HasValidTextureCompatibility() ||
!z.stencil_info.HasValidTextureCompatibility()) {
DepthFatal("invalid PS5 depth texture-compatibility encoding");
}
const bool attachment_unbound =
z.z_info.format == Prospero::DepthFormat::kInvalid &&
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
z.htile_data_base_addr == 0 &&
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
// presence alone must not manufacture a depth attachment.
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
z.size.y_max == 0;
if (attachment_unbound) {
static std::atomic_bool logged = false;
if (!logged.exchange(true, std::memory_order_relaxed)) {
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
}
return;
}
const bool has_htile = z.z_info.htile_acceleration;
const bool unsupported_shading_rate_encoding =
z.shading_rate_encoding > 1 || (z.shading_rate_encoding != 0 && !has_htile);
@@ -158,23 +125,16 @@ void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepth
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
z.depth_view.slice_max);
}
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
rc.copy_sample != 0 || z.z_info.expclear_enabled ||
z.stencil_info.expclear_enabled || z.z_info.partially_resident ||
if (z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
z.z_info.partially_resident ||
z.stencil_info.partially_resident || z.z_info.max_mip_level != 0 ||
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
z.z_read_base_addr == 0 ||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
(z.z_read_base_addr & 0xffffu) != 0 ||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
depth_address == 0 || (depth_address & 0xffffu) != 0) {
DepthFatal("unsupported depth register state");
}
if (has_stencil) {
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
z.stencil_read_base_addr == 0 ||
(!z.depth_view.stencil_write_disable &&
z.stencil_write_base_addr != z.stencil_read_base_addr) ||
(z.stencil_read_base_addr & 0xffffu) != 0) {
stencil_address == 0 || (stencil_address & 0xffffu) != 0) {
DepthFatal("unsupported stencil attachment state");
}
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
@@ -227,11 +187,92 @@ void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepth
const auto depth_backing_size = depth_size.size * view.image_layers;
const auto stencil_backing_size = stencil_size.size * view.image_layers;
const auto htile_backing_size = htile_size.size * view.image_layers;
if (!GuestRange {z.z_read_base_addr, depth_backing_size}.Valid() ||
(has_stencil && !GuestRange {z.stencil_read_base_addr, stencil_backing_size}.Valid()) ||
if (!GuestRange {depth_address, depth_backing_size}.Valid() ||
(has_stencil && !GuestRange {stencil_address, stencil_backing_size}.Valid()) ||
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
DepthFatal("layered depth backing range is invalid");
}
TextureCache::ImageDesc desc {};
desc.type = TextureCache::BindingType::DepthTarget;
desc.info.data = {depth_address, depth_backing_size};
desc.info.stencil =
has_stencil ? GuestRange {stencil_address, stencil_backing_size} : GuestRange {};
desc.info.pixel_format = format;
desc.info.guest_format = guest_format;
desc.info.type = Prospero::ImageType::kColor2D;
desc.info.extent = {width, height, 1};
desc.info.resources = {1, view.image_layers};
desc.info.pitch = pitch;
desc.info.bytes_per_block = bytes;
desc.info.samples = samples;
desc.info.tile_mode = Prospero::TileMode::kDepth;
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
desc.info.metadata.range =
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
desc.info.metadata.stencil_compressed =
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
desc.view_info.format = format;
desc.view_info.type =
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
desc.view_info.base_level = 0;
desc.view_info.level_count = 1;
desc.view_info.base_layer = view.base_layer;
desc.view_info.layer_count = view.layer_count;
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
return desc;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
KYTY_PROFILER_FUNCTION();
const auto& hw = buffer.GetRegisters();
const auto& z = hw.GetDepthRenderTarget();
const auto& rc = hw.GetRenderControl();
const auto& dc = hw.GetDepthControl();
const auto& sc = hw.GetStencilControl();
const auto& sm = hw.GetStencilMask();
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
rc.depth_clear_enable || rc.copy_depth_to_color;
const bool stencil_active =
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
if (!depth_active && !stencil_active) {
return;
}
const bool attachment_unbound =
z.z_info.format == Prospero::DepthFormat::kInvalid &&
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
z.htile_data_base_addr == 0 &&
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
// presence alone must not manufacture a depth attachment.
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
z.size.y_max == 0;
if (attachment_unbound) {
static std::atomic_bool logged = false;
if (!logged.exchange(true, std::memory_order_relaxed)) {
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
}
return;
}
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
rc.copy_sample != 0 || dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
(has_stencil && !z.depth_view.stencil_write_disable &&
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
DepthFatal("unsupported depth register state");
}
r.desc = MakeDepthTargetDesc(buffer, z);
r.depth_clear_enable = rc.depth_clear_enable;
r.depth_load_clear_enable = r.depth_clear_enable;
r.depth_clear_value = hw.GetDepthClearValue();
@@ -282,41 +323,73 @@ void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepth
r.stencil_dynamic_back = r.stencil_dynamic_front;
}
}
TextureCache::ImageDesc desc {};
desc.type = TextureCache::BindingType::DepthTarget;
desc.info.data = {z.z_read_base_addr, depth_backing_size};
desc.info.stencil =
has_stencil ? GuestRange {z.stencil_read_base_addr, stencil_backing_size} : GuestRange {};
desc.info.pixel_format = format;
desc.info.guest_format = guest_format;
desc.info.type = Prospero::ImageType::kColor2D;
desc.info.extent = {width, height, 1};
desc.info.resources = {1, view.image_layers};
desc.info.pitch = pitch;
desc.info.bytes_per_block = bytes;
desc.info.samples = samples;
desc.info.tile_mode = Prospero::TileMode::kDepth;
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
desc.info.metadata.range =
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
desc.info.metadata.stencil_compressed =
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
desc.view_info.format = format;
desc.view_info.type =
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
desc.view_info.base_level = 0;
desc.view_info.level_count = 1;
desc.view_info.base_layer = view.base_layer;
desc.view_info.layer_count = view.layer_count;
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
r.desc = std::move(desc);
auto& cache = m_context.GetTextureCache();
r.image_id = cache.FindImage(r.desc);
auto& cache = m_context.GetTextureCache();
r.image_id = cache.FindImage(r.desc);
BindRenderTarget(r.image_id);
}
bool RenderExecutor::DepthStencilCopy(CommandBuffer& buffer) {
const auto& hw = buffer.GetRegisters();
const auto& z = hw.GetDepthRenderTarget();
const auto& override = hw.GetDepthRenderOverride();
if (hw.GetColorControl().mode != 0) {
return false;
}
const bool depth_copy = override.force_z_dirty && override.force_z_valid &&
z.z_info.format != Prospero::DepthFormat::kInvalid &&
z.z_read_base_addr != 0 && z.z_write_base_addr != 0 &&
z.z_read_base_addr != z.z_write_base_addr;
const bool stencil_copy = override.force_stencil_dirty && override.force_stencil_valid &&
z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
z.stencil_read_base_addr != 0 && z.stencil_write_base_addr != 0 &&
z.stencil_read_base_addr != z.stencil_write_base_addr;
if (!depth_copy && !stencil_copy) {
return false;
}
auto read_desc = MakeDepthTargetDesc(buffer, z);
auto write_desc = MakeDepthTargetDesc(buffer, z, true);
auto& cache = m_context.GetTextureCache();
const auto read_id = cache.FindImage(read_desc);
BindRenderTarget(read_id);
const auto write_id = cache.FindImage(write_desc);
BindRenderTarget(write_id);
cache.UpdateImage(read_id);
cache.UpdateImage(write_id);
cache.MarkGpuWritten(write_id);
auto& source = cache.GetImage(read_id);
auto& destination = cache.GetImage(write_id);
EXIT_IF(read_id == write_id || source.backing.format != destination.backing.format);
auto& scheduler = m_context.GetCommandScheduler();
scheduler.EndRendering();
const auto command = scheduler.Current().Handle();
const ImageSubresourceRange range {read_desc.view_info.base_level, 1,
read_desc.view_info.base_layer,
read_desc.view_info.layer_count};
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
range, command);
destination.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
range, command);
std::array<vk::ImageCopy, 2> regions {};
uint32_t count = 0;
for (const auto aspect: {vk::ImageAspectFlagBits::eDepth, vk::ImageAspectFlagBits::eStencil}) {
if ((aspect == vk::ImageAspectFlagBits::eDepth && !depth_copy) ||
(aspect == vk::ImageAspectFlagBits::eStencil && !stencil_copy)) {
continue;
}
auto& region = regions[count++];
region.srcSubresource = {aspect, range.base_level, range.base_layer,
range.layer_count};
region.dstSubresource = region.srcSubresource;
region.extent = write_desc.info.extent;
}
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
count, regions.data());
return true;
}
vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
const auto format = desc.view_info.format;
if (format == vk::Format::eUndefined) {
+1
View File
@@ -183,6 +183,7 @@ private:
uint32_t render_target_slice_offset, uint32_t render_target_slot,
bool ignore_target_mask = false, bool exact_format = false);
void ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& target);
[[nodiscard]] bool DepthStencilCopy(CommandBuffer& buffer);
[[nodiscard]] bool PrepareDrawRenderState(CommandBuffer& buffer,
const DrawCallInfo& draw,
uint32_t render_target_slice_offset,
@@ -1238,7 +1238,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
return;
}
if (ConsumeMetadataColorOperation(buffer)) {
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
ResetBindings();
return;
}
@@ -1354,7 +1354,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
return;
}
if (ConsumeMetadataColorOperation(buffer)) {
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
ResetBindings();
return;
}
+316 -1
View File
@@ -504,6 +504,10 @@ struct RenderExecutorTestAccess {
executor.ResolveRenderDepthTarget(buffer, depth);
}
static bool DepthStencilCopy(RenderExecutor &executor, CommandBuffer &buffer) {
return executor.DepthStencilCopy(buffer);
}
static void ResolveRenderColorTarget(RenderExecutor &executor,
CommandBuffer &buffer,
RenderColorInfo &color, uint32_t slot) {
@@ -8941,7 +8945,7 @@ public:
void CheckRenderExecutorStencilBindingDiscovery() {
constexpr const char *name = "RenderExecutorStencilBindingDiscovery";
constexpr uintptr_t base = 0x0000000203600000ull;
constexpr uint64_t allocation_size = 0x180000;
constexpr uint64_t allocation_size = 0x200000;
constexpr uint64_t allocation_alignment = 0x10000;
constexpr uint64_t depth_address = base + 0x40000;
constexpr uint64_t stencil_address = base + 0x70000;
@@ -10087,6 +10091,261 @@ public:
DestroyBuffer(&bounds_readback);
registers.SetDepthClearValue(0.0f);
// DB override copies run with depth and stencil tests disabled.
constexpr uint64_t copy_source_depth = base + 0x180000;
constexpr uint64_t copy_source_stencil = base + 0x1a0000;
constexpr uint64_t copy_destination_depth = base + 0x1c0000;
constexpr uint64_t copy_destination_stencil = base + 0x1e0000;
constexpr uint32_t copy_side = 8;
constexpr uint32_t copy_layer_texels = copy_side * copy_side;
constexpr uint32_t copy_depth_bytes =
copy_layer_texels * 2 * sizeof(uint32_t);
constexpr uint32_t copy_readback_stride =
copy_depth_bytes + copy_layer_texels * 2;
HW::DepthRenderTarget copy_target{};
copy_target.z_info.format = Prospero::DepthFormat::kZ32F;
copy_target.z_info.texture_compatibility =
Prospero::TextureCompatiblePlaneCompression::kEnable;
copy_target.stencil_info.format = Prospero::StencilFormat::k8UInt;
copy_target.stencil_info.texture_compatibility =
Prospero::TextureCompatibleStencil::kEnable;
copy_target.stencil_info.htile_stencil_disabled = true;
copy_target.depth_view.slice_max = 1;
copy_target.size = {copy_side - 1, copy_side - 1, true};
HW::DepthControl copy_seed_control{};
copy_seed_control.z_enable = true;
copy_seed_control.z_write_enable = true;
copy_seed_control.stencil_enable = true;
registers.SetDepthControl(copy_seed_control);
registers.SetRenderControl({});
const auto make_copy_target = [&](uint64_t depth, uint64_t stencil) {
auto target = copy_target;
target.z_read_base_addr = depth;
target.z_write_base_addr = depth;
target.stencil_read_base_addr = stencil;
target.stencil_write_base_addr = stencil;
registers.SetDepthRenderTarget(target);
RenderDepthInfo resolved{};
RenderExecutorTestAccess::ResolveRenderDepthTarget(
executor, scheduler.Current(), resolved);
(void)texture_cache.FindDepthTarget(resolved.image_id, resolved.desc);
RenderExecutorTestAccess::ResetBindings(executor);
return resolved;
};
const auto copy_source =
make_copy_target(copy_source_depth, copy_source_stencil);
const auto copy_destination =
make_copy_target(copy_destination_depth, copy_destination_stencil);
const auto clear_copy_target = [&](ImageId id, bool source) {
auto &image = texture_cache.GetImage(id);
image.Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {},
scheduler.Current().Handle());
for (uint32_t layer = 0; layer < 2; ++layer) {
const vk::ClearDepthStencilValue clear{
source ? (layer == 0 ? 0.75f : 0.25f)
: (layer == 0 ? 0.875f : 0.625f),
source ? (layer == 0 ? 0xa3u : 0x5au)
: (layer == 0 ? 0x31u : 0xc4u)};
const vk::ImageSubresourceRange range{
vk::ImageAspectFlagBits::eDepth |
vk::ImageAspectFlagBits::eStencil,
0, 1, layer, 1};
scheduler.Current().Handle().clearDepthStencilImage(
image.backing.image, vk::ImageLayout::eTransferDstOptimal, &clear,
1, &range);
}
texture_cache.MarkGpuWritten(id);
};
struct CopyCase {
const char *label;
HW::DepthRenderOverride override;
uint32_t color_mode;
bool depth;
bool stencil;
};
constexpr std::array copy_cases{
CopyCase{"no override", {}, 0, false, false},
CopyCase{
"depth valid only", {true, false, false, false}, 0, false, false},
CopyCase{
"depth dirty only", {false, true, false, false}, 0, false, false},
CopyCase{"stencil valid only",
{false, false, true, false},
0,
false,
false},
CopyCase{"stencil dirty only",
{false, false, false, true},
0,
false,
false},
CopyCase{
"unpaired aspects", {true, false, false, true}, 0, false, false},
CopyCase{"color enabled", {true, true, true, true}, 1, false, false},
CopyCase{"depth only", {true, true, false, false}, 0, true, false},
CopyCase{"stencil only", {false, false, true, true}, 0, false, true},
CopyCase{
"depth and stencil", {true, true, true, true}, 0, true, true}};
auto copy_readback =
CreateHostBuffer(name, copy_cases.size() * copy_readback_stride,
vk::BufferUsageFlagBits::eTransferDst, {});
copy_target.z_read_base_addr = copy_source_depth;
copy_target.z_write_base_addr = copy_destination_depth;
copy_target.stencil_read_base_addr = copy_source_stencil;
copy_target.stencil_write_base_addr = copy_destination_stencil;
copy_target.depth_view.slice_start = 1;
registers.SetDepthRenderTarget(copy_target);
registers.SetDepthControl({});
RenderDepthInfo copy_disabled_depth{};
RenderExecutorTestAccess::ResolveRenderDepthTarget(
executor, scheduler.Current(), copy_disabled_depth);
Require(name, "copy without attachment tests",
!copy_disabled_depth.image_id,
"disabled depth/stencil tests acquired a regular attachment");
for (size_t index = 0; index < copy_cases.size(); ++index) {
const auto &test = copy_cases[index];
clear_copy_target(copy_source.image_id, true);
clear_copy_target(copy_destination.image_id, false);
registers.SetDepthRenderOverride(test.override);
auto color_control = registers.GetColorControl();
color_control.mode = test.color_mode;
registers.SetColorControl(color_control);
Require(name, test.label,
RenderExecutorTestAccess::DepthStencilCopy(
executor, scheduler.Current()) ==
(test.depth || test.stencil),
"DB override copy did not honor paired flags and disabled "
"color output");
RenderExecutorTestAccess::ResetBindings(executor);
const auto offset = index * copy_readback_stride;
const std::array<vk::BufferImageCopy, 2> copies{
{{offset,
0,
0,
{vk::ImageAspectFlagBits::eDepth, 0, 0, 2},
{},
copy_destination.desc.info.extent},
{offset + copy_depth_bytes,
0,
0,
{vk::ImageAspectFlagBits::eStencil, 0, 0, 2},
{},
copy_destination.desc.info.extent}}};
texture_cache.GetImage(copy_destination.image_id)
.Download(copies, copy_readback.buffer, 0, copy_readback.size);
}
scheduler.Current().Handle().pipelineBarrier2(bounds_dependency);
scheduler.Finish();
const auto copy_result = ReadBuffer(
name, copy_readback,
copy_cases.size() * copy_readback_stride / sizeof(uint32_t));
for (size_t index = 0; index < copy_cases.size(); ++index) {
const auto &test = copy_cases[index];
const auto *depth = copy_result.data() +
index * copy_readback_stride / sizeof(uint32_t);
const auto *stencil =
reinterpret_cast<const uint8_t *>(depth) + copy_depth_bytes;
for (uint32_t layer = 0; layer < 2; ++layer) {
const auto expected_depth =
std::bit_cast<uint32_t>(layer == 0 ? 0.875f
: test.depth ? 0.25f
: 0.625f);
const auto expected_stencil = layer == 0 ? 0x31u
: test.stencil ? 0x5au
: 0xc4u;
for (uint32_t texel = 0; texel < copy_layer_texels; ++texel) {
const auto position = layer * copy_layer_texels + texel;
Require(name, test.label,
depth[position] == expected_depth &&
stencil[position] == expected_stencil,
"DB override copy changed an unselected layer/aspect or "
"missed copied texels");
}
}
}
DestroyBuffer(&copy_readback);
ShaderTextureResource copy_sample_descriptor{};
copy_sample_descriptor.fields[0] =
static_cast<uint32_t>(copy_destination_depth >> 8u);
copy_sample_descriptor.fields[1] =
static_cast<uint32_t>(copy_destination_depth >> 40u) |
(static_cast<uint32_t>(Prospero::BufferFormat::k32Float) << 20u) |
(((copy_side - 1u) & 3u) << 30u);
copy_sample_descriptor.fields[2] =
((copy_side - 1u) >> 2u) | ((copy_side - 1u) << 14u);
copy_sample_descriptor.fields[3] =
DstSel(4, 4, 4, 4) |
(static_cast<uint32_t>(Prospero::TileMode::kDepth) << 20u) |
(static_cast<uint32_t>(Prospero::ImageType::kColor2DArray) << 28u);
copy_sample_descriptor.fields[4] = 1;
copy_sample_descriptor.fields[5] = 0x00700000u;
TestCase copy_sample_test;
copy_sample_test.name = "DepthOverrideR32Sampling";
copy_sample_test.has_user_data = true;
copy_sample_test.image_descriptor_swizzle =
copy_sample_descriptor.DstSelXYZW();
std::copy_n(copy_sample_descriptor.fields, 8,
copy_sample_test.user_data.begin());
copy_sample_test.user_data[50] = 2 * sizeof(uint32_t);
copy_sample_test.opcodes = {
ShaderOpcode::V_MOV_B32, ShaderOpcode::IMAGE_SAMPLE,
ShaderOpcode::BUFFER_STORE_DWORD, ShaderOpcode::S_ENDPGM};
copy_sample_test.required_spirv = {"OpImageSampleExplicitLod"};
for (uint32_t layer = 0; layer < 2; ++layer) {
AppendVMovLiteral(&copy_sample_test.code, 20,
std::bit_cast<uint32_t>(0.5f));
AppendVMovLiteral(&copy_sample_test.code, 21,
std::bit_cast<uint32_t>(0.5f));
AppendVMovLiteral(&copy_sample_test.code, 22,
std::bit_cast<uint32_t>(static_cast<float>(layer)));
copy_sample_test.code.push_back(EncodeMimg0(0x20, 1, 0, false, 5));
copy_sample_test.code.push_back(EncodeMimg1(0, 20, 0, 2));
AppendStoreVgpr(&copy_sample_test.code, 0, layer);
copy_sample_test.expected.push_back(
std::bit_cast<uint32_t>(layer == 0 ? 0.875f : 0.25f));
}
AppendEnd(&copy_sample_test.code);
const auto copy_sample_program =
CompileCase(copy_sample_test, SubgroupSize());
ShaderRecompiler::IR::DescriptorValue copy_sample_value{};
copy_sample_value.dword_count = 8;
std::copy_n(copy_sample_descriptor.fields, 8,
copy_sample_value.dwords.begin());
const auto copy_sample_binding = RenderExecutorTestAccess::ResolveTexture(
executor, copy_sample_program.program.info.images[0],
copy_sample_value);
const auto copy_sample_view = texture_cache.FindTexture(
copy_sample_binding.image_id, copy_sample_binding.desc);
Require(
name, "copied depth sampled owner",
copy_sample_binding.image_id == copy_destination.image_id &&
copy_sample_binding.desc.info.pixel_format ==
vk::Format::eR32Sfloat,
"R32 sampling did not reuse the GPU-written depth-copy destination");
auto &copy_sample_image = texture_cache.GetImage(copy_destination.image_id);
copy_sample_image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal,
vk::AccessFlagBits2::eShaderRead, {},
scheduler.Current().Handle());
Image copied_depth_sample;
copied_depth_sample.view = copy_sample_view;
copied_depth_sample.layout = copy_sample_image.backing.state.layout;
scheduler.Finish();
auto copy_sample_output = CreateStorageBuffer(name, {}, 2);
const auto copy_sampler = CreateNearestSampler(name);
Dispatch(copy_sample_test, copy_sample_program, copy_sample_output,
nullptr, &copied_depth_sample, nullptr, nullptr, copy_sampler);
Require(
name, "copied depth R32 samples",
ReadBuffer(name, copy_sample_output, 2) == copy_sample_test.expected,
"R32 post-effect sampling saw stale depth after the override copy");
m_device.destroySampler(copy_sampler);
DestroyBuffer(&copy_sample_output);
RenderExecutorTestAccess::ResetBindings(executor);
registers.SetDepthRenderOverride({});
registers.SetColorControl({});
auto shared_depth_descriptor = sampled_depth_descriptor;
shared_depth_descriptor.fields[0] =
static_cast<uint32_t>(phased_depth_address >> 8u);
@@ -13118,6 +13377,17 @@ private:
m_runtime_context.queue = m_queue;
m_runtime_context.attachment_feedback_loop_enabled = true;
m_runtime_context.provoking_vertex_last_enabled = true;
const vk::PhysicalDeviceImageFormatInfo2 block_texel_view_info{
.format = vk::Format::eBc1RgbaUnormBlock,
.type = vk::ImageType::e2D,
.tiling = vk::ImageTiling::eOptimal,
.usage = vk::ImageUsageFlagBits::eSampled,
.flags = vk::ImageCreateFlagBits::eBlockTexelViewCompatible,
};
const auto block_texel_view_props =
m_physical_device.getImageFormatProperties2(block_texel_view_info);
m_runtime_context.supports_block_texel_view =
block_texel_view_props.result == vk::Result::eSuccess;
VmaVulkanFunctions functions{};
functions.vkGetInstanceProcAddr =
@@ -27947,6 +28217,49 @@ void CheckPm4DepthControlHighBits(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", "Pm4DepthControlHighBits");
}
void CheckPm4DepthRenderOverride(RenderContext &renderer) {
constexpr const char *name = "Pm4DepthRenderOverride";
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
struct Case {
uint32_t value;
HW::DepthRenderOverride expected;
};
constexpr std::array cases{Case{0x28000000u, {true, true, false, false}},
Case{0x50000000u, {false, false, true, true}},
Case{0x18000000u, {false, true, false, true}},
Case{0x60000000u, {true, false, true, false}},
Case{0xf8000000u, {true, true, true, true}},
Case{0x82001a0fu, {false, false, false, false}}};
for (const bool indirect : {false, true}) {
for (const auto &test : cases) {
std::array<uint32_t, 2> registers{Pm4::DB_RENDER_OVERRIDE, test.value};
const auto address = reinterpret_cast<uint64_t>(registers.data());
const std::array<uint32_t, 3> direct_packet{
KYTY_PM4(3, Pm4::IT_SET_CONTEXT_REG, Pm4::R_ZERO),
Pm4::DB_RENDER_OVERRIDE, test.value};
const std::array<uint32_t, 5> indirect_packet{
KYTY_PM4(5, Pm4::IT_SET_CONTEXT_REG_INDIRECT, Pm4::R_ZERO),
static_cast<uint32_t>(address), static_cast<uint32_t>(address >> 32u),
0x80000000u, 1u};
Pm4Execution execution;
const auto result = processor.Process(
execution, indirect ? std::span<const uint32_t>{indirect_packet}
: std::span<const uint32_t>{direct_packet});
const auto &actual = processor.GetCtx().GetDepthRenderOverride();
Require(
name, indirect ? "indirect register" : "direct register",
result == Pm4ProcessResult::Complete &&
actual.force_z_valid == test.expected.force_z_valid &&
actual.force_z_dirty == test.expected.force_z_dirty &&
actual.force_stencil_valid == test.expected.force_stencil_valid &&
actual.force_stencil_dirty == test.expected.force_stencil_dirty,
"DB_RENDER_OVERRIDE lost, exchanged, or retained force flags");
}
}
std::printf("[host] %-32s ok\n", name);
}
void CheckAgcShaderFusion() {
struct Case {
bool gs;
@@ -28721,6 +29034,7 @@ int main(int argc, char **argv) {
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
CheckPm4DepthRenderOverride(vulkan.RuntimeRenderer());
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
return 0;
}
@@ -28931,6 +29245,7 @@ int main(int argc, char **argv) {
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
CheckPm4DepthRenderOverride(vulkan.RuntimeRenderer());
CheckAgcShaderFusion();
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());