Track depth and stencil attachment feedback across graphics stages

Co-authored-by: TarkusR <90155059+TarkusR@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-24 02:25:58 +04:00
parent 51c34d1a24
commit 66cc2fe26d
5 changed files with 293 additions and 66 deletions
@@ -41,13 +41,42 @@ struct RenderDepthInfo {
[[nodiscard]] vk::ImageAspectFlags AttachmentWriteAspects() const;
};
inline vk::ImageAspectFlags DepthFeedbackAspects(vk::ImageAspectFlags draw_writes,
const ImageViewInfo& target,
const ImageViewInfo& sampled) {
if (!ImageRangeOverlaps(target.base_level, target.level_count, sampled.base_level,
sampled.level_count) ||
!ImageRangeOverlaps(target.base_layer, target.layer_count, sampled.base_layer,
sampled.layer_count)) {
return {};
}
return draw_writes & sampled.aspect;
}
inline vk::ImageAspectFlags DepthReadableAspects(vk::ImageLayout layout) {
switch (layout) {
case vk::ImageLayout::eDepthReadOnlyOptimal:
case vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal:
return vk::ImageAspectFlagBits::eDepth;
case vk::ImageLayout::eStencilReadOnlyOptimal:
case vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal:
return vk::ImageAspectFlagBits::eStencil;
case vk::ImageLayout::eDepthStencilReadOnlyOptimal:
case vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT:
case vk::ImageLayout::eGeneral:
return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
default:
return {};
}
}
inline vk::ImageLayout depth_attachment_layout(const RenderDepthInfo& depth) {
const auto available = ImageViewOps::DepthAspectMask(depth.desc.view_info.format);
const auto writes = depth.AttachmentWriteAspects();
const bool has_depth = static_cast<bool>(available & vk::ImageAspectFlagBits::eDepth);
const bool has_stencil = static_cast<bool>(available & vk::ImageAspectFlagBits::eStencil);
// LoadOp clears are separate from guest depth writes.
const bool depth_write = depth.depth_write_enable;
// The attachment layout must permit load clears as well as draw writes.
const bool depth_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eDepth);
const bool stencil_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eStencil);
if (!has_stencil) {
return depth_write ? vk::ImageLayout::eDepthAttachmentOptimal
@@ -17,6 +17,7 @@
#include "graphics/host_gpu/hostMemory.h"
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
@@ -989,19 +990,7 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
std::ranges::find(image.views, binding.image_view, &CachedImageView::view);
EXIT_IF(storage || host_view == image.views.end());
const auto aspect = host_view->info.aspect;
const bool depth_feedback =
layout == vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT &&
program.stage == ShaderType::Pixel;
const bool depth_read =
depth_feedback || layout == vk::ImageLayout::eDepthReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthStencilReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal;
const bool stencil_read =
layout == vk::ImageLayout::eStencilReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthStencilReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal;
if ((aspect & vk::ImageAspectFlagBits::eDepth && !depth_read) ||
(aspect & vk::ImageAspectFlagBits::eStencil && !stencil_read)) {
if (aspect & ~DepthReadableAspects(layout)) {
EXIT("sampling a writable depth/stencil attachment aspect\n");
}
}
+2 -1
View File
@@ -196,7 +196,8 @@ private:
bool primitive_restart_enable);
[[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
uint32_t color_count, RenderDepthInfo& depth,
const std::optional<PreparedBindings>& pixel = std::nullopt);
vk::ImageAspectFlags& feedback_aspects,
std::span<PreparedBindings* const> stages = {});
[[nodiscard]] bool ResolveColorTargets(CommandBuffer& buffer,
uint32_t render_target_slice_offset);
void BindImage(ImageId id, bool storage);
+27 -27
View File
@@ -453,8 +453,10 @@ struct DrawCallInfo {
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
uint32_t color_count, RenderDepthInfo& depth,
const std::optional<PreparedBindings>& pixel) {
vk::ImageAspectFlags& feedback_aspects,
std::span<PreparedBindings* const> stages) {
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
feedback_aspects = {};
auto& cache = m_context.GetTextureCache();
RenderState state {};
state.width = std::numeric_limits<uint32_t>::max();
@@ -529,28 +531,29 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
image.info.data.address, depth.desc.view_info.base_layer,
depth.desc.view_info.layer_count);
EXIT_IF(image_view == nullptr || image.backing.samples != depth.desc.info.samples);
const bool feedback = depth.depth_write_enable && pixel &&
std::ranges::any_of(pixel->images, [&](const TextureBinding& binding) {
if (binding.image_id != depth.image_id ||
binding.desc.type != TextureCache::BindingType::Texture) {
return false;
}
const auto native =
std::ranges::find(image.views, binding.image_view, &CachedImageView::view);
EXIT_IF(native == image.views.end());
const auto& sampled = native->info;
const auto& target = depth.desc.view_info;
return (sampled.aspect & vk::ImageAspectFlagBits::eDepth) &&
ImageRangeOverlaps(sampled.base_level, sampled.level_count,
target.base_level, target.level_count) &&
ImageRangeOverlaps(sampled.base_layer, sampled.layer_count,
target.base_layer, target.layer_count);
});
if (feedback && !m_context.GetGraphics().attachment_feedback_loop_enabled) {
const auto draw_writes = depth.AttachmentWriteAspects();
vk::ImageAspectFlags sampled_aspects;
for (const auto* stage: stages) {
for (const auto& binding: stage->images) {
if (binding.image_id != depth.image_id ||
binding.desc.type != TextureCache::BindingType::Texture) continue;
const auto native =
std::ranges::find(image.views, binding.image_view, &CachedImageView::view);
EXIT_IF(native == image.views.end());
sampled_aspects |= native->info.aspect;
feedback_aspects |= DepthFeedbackAspects(draw_writes, depth.desc.view_info,
native->info);
}
}
if (feedback_aspects && !m_context.GetGraphics().attachment_feedback_loop_enabled) {
EXIT("depth attachment feedback loop is not supported by the host\n");
}
const auto layout = feedback ? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
: depth_attachment_layout(depth);
auto layout = depth_attachment_layout(depth);
if (sampled_aspects & ~DepthReadableAspects(layout)) {
layout = m_context.GetGraphics().attachment_feedback_loop_enabled
? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
: vk::ImageLayout::eGeneral;
}
// The attachment store writes even when guest depth/stencil tests do not.
const auto access = vk::AccessFlagBits2::eDepthStencilAttachmentRead |
vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
@@ -1100,9 +1103,10 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
std::span {state.color_info, state.color_count}, state.depth_info, vertex_stages, buffer,
state.ps_active ? &state.ps_input_info : nullptr, topology, primitive_restart_enable,
state.programs);
vk::ImageAspectFlags feedback_aspects;
const auto rendering =
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info,
bindings.pixel);
feedback_aspects, stages);
// Resource preparation above may synchronously finish and restart the scheduler. From this
// point onward, every operation targets the current command buffer and cannot touch guest
@@ -1134,11 +1138,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
SetGraphicsDynamicParams(buffer, vk_buffer, vertex_stages.back(), state.depth_info, rendering);
if (m_context.GetGraphics().attachment_feedback_loop_enabled) {
vk_buffer.setAttachmentFeedbackLoopEnableEXT(
rendering.depth_stencil_attachment.image_layout ==
vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
? vk::ImageAspectFlags {vk::ImageAspectFlagBits::eDepth}
: vk::ImageAspectFlags {});
vk_buffer.setAttachmentFeedbackLoopEnableEXT(feedback_aspects);
}
LogDrawPhase(draw.Name(), "BeginRendering");