diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index f3369cecb..4bbf958f9 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -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); diff --git a/src/graphics/guest_gpu/hardwareContext.h b/src/graphics/guest_gpu/hardwareContext.h index c52693ec1..e9fcede00 100644 --- a/src/graphics/guest_gpu/hardwareContext.h +++ b/src/graphics/guest_gpu/hardwareContext.h @@ -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; diff --git a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp index 7d6426b29..2f193d7ff 100644 --- a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp @@ -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(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(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 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) { diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 730b9ac14..22499d65b 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -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, diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 2bc17bf28..04e31da6e 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -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; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 2f8397874..84d618d05 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -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 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(depth) + copy_depth_bytes; + for (uint32_t layer = 0; layer < 2; ++layer) { + const auto expected_depth = + std::bit_cast(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(©_readback); + + ShaderTextureResource copy_sample_descriptor{}; + copy_sample_descriptor.fields[0] = + static_cast(copy_destination_depth >> 8u); + copy_sample_descriptor.fields[1] = + static_cast(copy_destination_depth >> 40u) | + (static_cast(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(Prospero::TileMode::kDepth) << 20u) | + (static_cast(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(©_sample_test.code, 20, + std::bit_cast(0.5f)); + AppendVMovLiteral(©_sample_test.code, 21, + std::bit_cast(0.5f)); + AppendVMovLiteral(©_sample_test.code, 22, + std::bit_cast(static_cast(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(©_sample_test.code, 0, layer); + copy_sample_test.expected.push_back( + std::bit_cast(layer == 0 ? 0.875f : 0.25f)); + } + AppendEnd(©_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 ©_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(©_sample_output); + RenderExecutorTestAccess::ResetBindings(executor); + registers.SetDepthRenderOverride({}); + registers.SetColorControl({}); + auto shared_depth_descriptor = sampled_depth_descriptor; shared_depth_descriptor.fields[0] = static_cast(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 registers{Pm4::DB_RENDER_OVERRIDE, test.value}; + const auto address = reinterpret_cast(registers.data()); + const std::array direct_packet{ + KYTY_PM4(3, Pm4::IT_SET_CONTEXT_REG, Pm4::R_ZERO), + Pm4::DB_RENDER_OVERRIDE, test.value}; + const std::array indirect_packet{ + KYTY_PM4(5, Pm4::IT_SET_CONTEXT_REG_INDIRECT, Pm4::R_ZERO), + static_cast(address), static_cast(address >> 32u), + 0x80000000u, 1u}; + Pm4Execution execution; + const auto result = processor.Process( + execution, indirect ? std::span{indirect_packet} + : std::span{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());