From 66cc2fe26d7c503627f6900a2aa0f9ee2abfbb0d Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 24 Sep 2026 02:25:58 +0400 Subject: [PATCH] Track depth and stencil attachment feedback across graphics stages Co-authored-by: TarkusR <90155059+TarkusR@users.noreply.github.com> --- .../host_gpu/renderer/depthRenderTarget.h | 33 ++- .../renderer/pipeline/descriptors.cpp | 15 +- src/graphics/host_gpu/renderer/render.h | 3 +- src/graphics/host_gpu/renderer/renderDraw.cpp | 54 ++-- tests/ShaderRecompilerComputeTests.cpp | 254 ++++++++++++++++-- 5 files changed, 293 insertions(+), 66 deletions(-) diff --git a/src/graphics/host_gpu/renderer/depthRenderTarget.h b/src/graphics/host_gpu/renderer/depthRenderTarget.h index 3f7791bc..6b3ff0d8 100644 --- a/src/graphics/host_gpu/renderer/depthRenderTarget.h +++ b/src/graphics/host_gpu/renderer/depthRenderTarget.h @@ -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(available & vk::ImageAspectFlagBits::eDepth); const bool has_stencil = static_cast(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(writes & vk::ImageAspectFlagBits::eDepth); const bool stencil_write = static_cast(writes & vk::ImageAspectFlagBits::eStencil); if (!has_stencil) { return depth_write ? vk::ImageLayout::eDepthAttachmentOptimal diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 68a59f57..5231caf3 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -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"); } } diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 9d04104f..b04101c4 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -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& pixel = std::nullopt); + vk::ImageAspectFlags& feedback_aspects, + std::span stages = {}); [[nodiscard]] bool ResolveColorTargets(CommandBuffer& buffer, uint32_t render_target_slice_offset); void BindImage(ImageId id, bool storage); diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 70cd9ed4..97dbac17 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -453,8 +453,10 @@ struct DrawCallInfo { RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, - const std::optional& pixel) { + vk::ImageAspectFlags& feedback_aspects, + std::span 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::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"); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 65e6ab3c..f5a79cbe 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -543,8 +543,15 @@ struct RenderExecutorTestAccess { RenderColorInfo *colors, uint32_t color_count, RenderDepthInfo &depth, - const std::optional &pixel = std::nullopt) { - return executor.AcquireRenderTargets(buffer, colors, color_count, depth, pixel); + std::span stages = {}, + vk::ImageAspectFlags *feedback_out = nullptr) { + vk::ImageAspectFlags feedback; + auto state = executor.AcquireRenderTargets(buffer, colors, color_count, depth, + feedback, stages); + if (feedback_out != nullptr) { + *feedback_out = feedback; + } + return state; } static void ResetBindings(RenderExecutor &executor) { @@ -10886,6 +10893,95 @@ public: "descriptor layouts were queried after a later mip transition " "instead of being captured at each binding"); + auto disjoint_depth_desc = split_desc; + disjoint_depth_desc.type = BindingType::DepthTarget; + disjoint_depth_desc.info.data.address = base + 0xe00000; + disjoint_depth_desc.info.pixel_format = vk::Format::eD32Sfloat; + disjoint_depth_desc.info.guest_format = Prospero::BufferFormat::k32Float; + disjoint_depth_desc.view_info.format = vk::Format::eD32Sfloat; + disjoint_depth_desc.view_info.aspect = vk::ImageAspectFlagBits::eDepth; + disjoint_depth_desc.view_info.level_count = 1; + disjoint_depth_desc.view_info.usage = + vk::ImageUsageFlagBits::eDepthStencilAttachment; + const auto disjoint_depth_id = texture_cache.FindImage(disjoint_depth_desc); + auto disjoint_sampled_desc = disjoint_depth_desc; + disjoint_sampled_desc.type = BindingType::Texture; + disjoint_sampled_desc.view_info.base_level = 1; + disjoint_sampled_desc.view_info.usage = vk::ImageUsageFlagBits::eSampled; + const auto disjoint_sampled_view = + texture_cache.FindTexture(disjoint_depth_id, disjoint_sampled_desc); + RenderDepthInfo disjoint_depth{}; + disjoint_depth.desc = disjoint_depth_desc; + disjoint_depth.image_id = disjoint_depth_id; + disjoint_depth.depth_test_enable = true; + disjoint_depth.depth_write_enable = true; + disjoint_depth.depth_compare_op = vk::CompareOp::eAlways; + PreparedBindings disjoint_binding{}; + disjoint_binding.runtime = &sampled_runtime; + disjoint_binding.images.push_back( + {disjoint_depth_id, disjoint_sampled_view, disjoint_sampled_desc}); + RenderExecutorTestAccess::BindRenderTarget(executor, disjoint_depth_id); + vk::ImageAspectFlags disjoint_feedback; + std::array disjoint_stages{&disjoint_binding}; + RenderColorInfo no_disjoint_color{}; + const auto disjoint_rendering = RenderExecutorTestAccess::AcquireRenderTargets( + executor, scheduler.Current(), &no_disjoint_color, 0, disjoint_depth, + disjoint_stages, &disjoint_feedback); + descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings( + executor, scheduler.Current(), disjoint_binding)); + const auto &disjoint_image = texture_cache.GetImage(disjoint_depth_id); + Require(name, "disjoint depth sampling during depth writes", + !disjoint_feedback && + disjoint_rendering.depth_stencil_attachment.image_layout == + vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT && + disjoint_binding.images[0].layout == + disjoint_rendering.depth_stencil_attachment.image_layout && + disjoint_image.backing.state.layout == + disjoint_rendering.depth_stencil_attachment.image_layout && + disjoint_image.backing.subresource_states.empty(), + "disjoint sampling used inconsistent image layouts or enabled feedback"); + RenderExecutorTestAccess::ResetBindings(executor); + + auto overlapping_sampled_desc = disjoint_sampled_desc; + overlapping_sampled_desc.view_info.base_level = 0; + const auto overlapping_view = texture_cache.FindTexture( + disjoint_depth_id, overlapping_sampled_desc); + ShaderRecompiler::IR::Program depth_views_ir{}; + depth_views_ir.stage = ShaderType::Pixel; + depth_views_ir.resource_tracking_complete = true; + depth_views_ir.info.images = {sampled_resource, sampled_resource}; + allocate_bindings(depth_views_ir); + ShaderRecompiler::IR::CompiledShaderInfo depth_views_program{}; + depth_views_program.stage = depth_views_ir.stage; + depth_views_program.info = std::move(depth_views_ir.info); + depth_views_program.bindings = std::move(depth_views_ir.bindings); + ShaderRecompiler::IR::ResourceSnapshot depth_views_snapshot; + ShaderStageRuntime depth_views_runtime{&depth_views_program, &depth_views_snapshot}; + PreparedBindings depth_views_binding{}; + depth_views_binding.runtime = &depth_views_runtime; + depth_views_binding.images.push_back( + {disjoint_depth_id, overlapping_view, overlapping_sampled_desc}); + depth_views_binding.images.push_back( + {disjoint_depth_id, disjoint_sampled_view, disjoint_sampled_desc}); + RenderExecutorTestAccess::BindRenderTarget(executor, disjoint_depth_id); + std::array depth_views_stages{&depth_views_binding}; + vk::ImageAspectFlags depth_views_feedback; + const auto depth_views_rendering = RenderExecutorTestAccess::AcquireRenderTargets( + executor, scheduler.Current(), &no_disjoint_color, 0, disjoint_depth, + depth_views_stages, &depth_views_feedback); + descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings( + executor, scheduler.Current(), depth_views_binding)); + Require(name, "overlapping and disjoint depth views", + overlapping_view != nullptr && + depth_views_feedback == vk::ImageAspectFlagBits::eDepth && + depth_views_rendering.depth_stencil_attachment.image_layout == + vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT && + MakeImageInfo(depth_views_binding.images[0]).imageLayout == + depth_views_rendering.depth_stencil_attachment.image_layout && + MakeImageInfo(depth_views_binding.images[1]).imageLayout == + depth_views_rendering.depth_stencil_attachment.image_layout, + "two depth views chose different descriptor layouts or feedback aspects"); + Libs::LibKernel::Memory::WriteBacking( storage_address, &storage_stale_value, sizeof(storage_stale_value)); texture_cache.ProcessDownloadImages(); @@ -11212,26 +11308,35 @@ public: executor, scheduler.Current(), bounds_depth); auto bounds_bindings = RenderExecutorTestAccess::PrepareGraphicsBindings( executor, bounds_vertex, bounds_pixel, true); + std::array bounds_stages{ + &bounds_bindings.vertex[0], &*bounds_bindings.pixel}; + vk::ImageAspectFlags bounds_feedback; const auto bounds_rendering = RenderExecutorTestAccess::AcquireRenderTargets( - executor, scheduler.Current(), &no_color, 0, bounds_depth, bounds_bindings.pixel); + executor, scheduler.Current(), &no_color, 0, bounds_depth, + bounds_stages, &bounds_feedback); descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings( executor, scheduler.Current(), bounds_bindings.vertex[0], *bounds_bindings.pixel)); const auto &vertex_depth = bounds_bindings.vertex[0].images[0]; const auto &pixel_depth = bounds_bindings.pixel->images[0]; - constexpr auto readonly_layout = vk::ImageLayout::eDepthReadOnlyOptimal; - Require(name, "deferred clear with sampled read-only depth bounds", + const auto expected_layout = pass == 0 + ? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT + : vk::ImageLayout::eDepthReadOnlyOptimal; + Require(name, "deferred clear with sampled depth bounds", bounds_depth.image_id == depth_only.image_id && bounds_depth.depth_bounds_test_enable && !bounds_depth.depth_write_enable && bounds_depth.depth_load_clear_enable == (pass == 0) && !texture_cache.IsMetaCleared(depth_only_htile_address, 0) && - bounds_rendering.depth_stencil_attachment.image_layout == readonly_layout && + bounds_feedback == (pass == 0 + ? vk::ImageAspectFlagBits::eDepth + : vk::ImageAspectFlags{}) && + bounds_rendering.depth_stencil_attachment.image_layout == expected_layout && bounds_rendering.depth_stencil_attachment.depth_clear == (pass == 0) && vertex_depth.image_id == depth_only.image_id && pixel_depth.image_id == depth_only.image_id && - MakeImageInfo(vertex_depth).imageLayout == readonly_layout && - MakeImageInfo(pixel_depth).imageLayout == readonly_layout, - "deferred clear changed guest depth writes, sampled layouts, or repeated"); + MakeImageInfo(vertex_depth).imageLayout == expected_layout && + MakeImageInfo(pixel_depth).imageLayout == expected_layout, + "a deferred clear sampled by vertex and pixel stages missed depth feedback"); scheduler.BeginRendering(bounds_rendering); scheduler.EndRendering(); RenderExecutorTestAccess::ResetBindings(executor); @@ -11554,35 +11659,50 @@ public: auto shared_bindings = RenderExecutorTestAccess::PrepareGraphicsBindings( executor, shared_depth_vertex, shared_depth_pixel, true); + auto& shared_image = texture_cache.GetImage(shared_depth.image_id); + auto stencil_view_info = shared_depth.desc.view_info; + stencil_view_info.aspect = vk::ImageAspectFlagBits::eStencil; + stencil_view_info.usage = vk::ImageUsageFlagBits::eSampled; + const auto stencil_view = shared_image.FindView(stencil_view_info); + auto& pixel_image = shared_bindings.pixel->images[0]; + pixel_image.image_view = stencil_view; + pixel_image.desc.view_info = stencil_view_info; + std::array shared_stages{ + &shared_bindings.vertex[0], &*shared_bindings.pixel}; + vk::ImageAspectFlags shared_feedback; const auto shared_rendering = RenderExecutorTestAccess::AcquireRenderTargets( - executor, scheduler.Current(), &no_color, 0, shared_depth, shared_bindings.pixel); + executor, scheduler.Current(), &no_color, 0, shared_depth, + shared_stages, &shared_feedback); descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings( executor, scheduler.Current(), shared_bindings.vertex[0], *shared_bindings.pixel)); const auto expected_layout = stencil_write - ? vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal + ? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT : vk::ImageLayout::eDepthStencilReadOnlyOptimal; const auto expected_access = vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eDepthStencilAttachmentRead | vk::AccessFlagBits2::eDepthStencilAttachmentWrite; - const auto &shared_image = texture_cache.GetImage(shared_depth.image_id); const auto &vertex_image = shared_bindings.vertex[0].images[0]; - const auto &pixel_image = shared_bindings.pixel->images[0]; Require(name, "sampled depth and stencil attachment layout", shared_depth.image_id == phased_depth.image_id && + stencil_view != nullptr && + shared_feedback == (stencil_write + ? vk::ImageAspectFlagBits::eStencil + : vk::ImageAspectFlags{}) && vertex_image.image_id == shared_depth.image_id && pixel_image.image_id == shared_depth.image_id && + pixel_image.image_view == stencil_view && MakeImageInfo(vertex_image).imageLayout == expected_layout && MakeImageInfo(pixel_image).imageLayout == expected_layout && shared_rendering.depth_stencil_attachment.image_layout == expected_layout && shared_image.backing.state.layout == expected_layout && shared_image.backing.state.access_mask == expected_access, - "a shader alias replaced the depth/stencil attachment layout " - "or dropped an attachment access"); + "stencil sampling and writes lost the feedback aspect, shared " + "layout, or attachment access"); scheduler.BeginRendering(shared_rendering); scheduler.EndRendering(); RenderExecutorTestAccess::ResetBindings(executor); @@ -13501,12 +13621,18 @@ public: !depth_feedback || bindings.pixel->images[0].image_id == depth.image_id, "the fragment must sample the depth attachment's native image"); auto &command = scheduler.Current(); + vk::ImageAspectFlags feedback_aspects; + std::array stages{ + &bindings.vertex[0], &*bindings.pixel}; auto rendering = RenderExecutorTestAccess::AcquireRenderTargets( - executor, command, &color, 1, depth, bindings.pixel); + executor, command, &color, 1, depth, stages, &feedback_aspects); const bool feedback_enabled = rendering.depth_stencil_attachment.image_layout == vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT; Require(name, "per-draw depth feedback", - feedback_enabled == (depth_feedback && depth.depth_write_enable), + feedback_enabled == (depth_feedback && depth.depth_write_enable) && + feedback_aspects == (feedback_enabled + ? vk::ImageAspectFlagBits::eDepth + : vk::ImageAspectFlags{}), "feedback was not selected only for an overlapping fragment depth read/write"); RenderExecutorTestAccess::CommitBindings( executor, command, selected, bindings.vertex[0], *bindings.pixel); @@ -13539,9 +13665,7 @@ public: } const vk::Bool32 write = true; cmd.setColorWriteEnableEXT(1, &write); - cmd.setAttachmentFeedbackLoopEnableEXT( - feedback_enabled ? vk::ImageAspectFlags{vk::ImageAspectFlagBits::eDepth} - : vk::ImageAspectFlags{}); + cmd.setAttachmentFeedbackLoopEnableEXT(feedback_aspects); const vk::DeviceSize offset = 0; cmd.bindVertexBuffers(0, 1, &buffer.buffer, &offset); cmd.draw(vertex_count, 1, 0, 0); @@ -31083,6 +31207,81 @@ void CheckDepthAttachmentWrites() { std::printf("[host] %-32s ok\n", "DepthAttachmentWrites"); } +void CheckDepthFeedbackAspects() { + RenderDepthInfo target{}; + target.desc.view_info.format = vk::Format::eD32SfloatS8Uint; + target.desc.view_info.base_level = 1; + target.desc.view_info.level_count = 2; + target.desc.view_info.base_layer = 3; + target.desc.view_info.layer_count = 2; + target.depth_test_enable = true; + target.depth_write_enable = true; + target.depth_compare_op = vk::CompareOp::eAlways; + target.stencil_test_enable = true; + target.stencil_front = {vk::StencilOp::eKeep, vk::StencilOp::eReplace, + vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0xff, 0xff, 0}; + target.stencil_back = target.stencil_front; + + auto sampled = target.desc.view_info; + const auto feedback = [&](const ImageViewInfo& view) { + return DepthFeedbackAspects(target.AttachmentWriteAspects(), + target.desc.view_info, view); + }; + sampled.aspect = vk::ImageAspectFlagBits::eDepth; + Require("DepthFeedbackAspects", "sampled writable depth", + feedback(sampled) == vk::ImageAspectFlagBits::eDepth, + "overlapping depth sampling did not request depth feedback"); + sampled.aspect = vk::ImageAspectFlagBits::eStencil; + Require("DepthFeedbackAspects", "sampled writable stencil", + feedback(sampled) == vk::ImageAspectFlagBits::eStencil, + "overlapping stencil sampling did not request stencil feedback"); + sampled.aspect = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil; + Require("DepthFeedbackAspects", "both writable aspects", + feedback(sampled) == sampled.aspect, + "combined depth/stencil sampling lost a writable aspect"); + + target.depth_write_enable = false; + Require("DepthFeedbackAspects", "read-only depth", + feedback(sampled) == vk::ImageAspectFlagBits::eStencil, + "read-only depth was included in stencil feedback"); + target.depth_write_enable = true; + target.stencil_test_enable = false; + Require("DepthFeedbackAspects", "read-only stencil", + feedback(sampled) == vk::ImageAspectFlagBits::eDepth, + "read-only stencil was included in depth feedback"); + + sampled.aspect = vk::ImageAspectFlagBits::eStencil; + Require("DepthFeedbackAspects", "sampled read-only aspect", + !feedback(sampled), + "sampling a read-only stencil requested feedback for depth writes"); + sampled.aspect = vk::ImageAspectFlagBits::eDepth; + sampled.base_level = 3; + Require("DepthFeedbackAspects", "disjoint mip", + !feedback(sampled), + "disjoint mip ranges requested depth feedback"); + sampled.base_level = 2; + sampled.base_layer = 5; + Require("DepthFeedbackAspects", "disjoint layer", + !feedback(sampled), + "disjoint layer ranges requested depth feedback"); + sampled.base_layer = 4; + Require("DepthFeedbackAspects", "overlapping range boundary", + feedback(sampled) == vk::ImageAspectFlagBits::eDepth, + "overlapping mip and layer boundaries missed depth feedback"); + target.depth_write_enable = false; + target.depth_load_clear_enable = true; + Require("DepthFeedbackAspects", "depth clear only", + feedback(sampled) == vk::ImageAspectFlagBits::eDepth, + "a sampled depth attachment clear missed feedback"); + target.depth_load_clear_enable = false; + target.stencil_clear_enable = true; + sampled.aspect = vk::ImageAspectFlagBits::eStencil; + Require("DepthFeedbackAspects", "stencil clear only", + feedback(sampled) == vk::ImageAspectFlagBits::eStencil, + "a sampled stencil attachment clear missed feedback"); + std::printf("[host] %-32s ok\n", "DepthFeedbackAspects"); +} + void CheckDynamicRenderingState() { RenderState first{}; first.width = 64; @@ -31130,10 +31329,10 @@ void CheckDynamicRenderingState() { vk::ImageLayout::eDepthStencilReadOnlyOptimal, "fully read-only depth/stencil used a writable layout"); attachment.depth_load_clear_enable = true; - Require("DynamicRenderingState", "read-only deferred depth clear", + Require("DynamicRenderingState", "deferred depth clear layout", depth_attachment_layout(attachment) == - vk::ImageLayout::eDepthStencilReadOnlyOptimal, - "a load clear changed the guest depth-write layout"); + vk::ImageLayout::eDepthAttachmentStencilReadOnlyOptimal, + "a depth load clear used a read-only attachment layout"); attachment.depth_test_enable = true; attachment.depth_write_enable = true; Require("DynamicRenderingState", "depth-write stencil-read layout", @@ -33153,6 +33352,7 @@ int main(int argc, char **argv) { } if (argc == 2 && std::strcmp(argv[1], "--image-overlap-only") == 0) { CheckDepthAttachmentWrites(); + CheckDepthFeedbackAspects(); CheckDynamicRenderingState(); VulkanHarness vulkan; vulkan.CheckRasterization(false); @@ -33170,6 +33370,12 @@ int main(int argc, char **argv) { CheckEmbeddedFetchVertexOffset(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--depth-feedback-only") == 0) { + CheckDepthAttachmentWrites(); + CheckDepthFeedbackAspects(); + CheckDynamicRenderingState(); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--polygon-mode-only") == 0) { VulkanHarness vulkan; vulkan.CheckRasterization(false); @@ -33325,6 +33531,7 @@ int main(int argc, char **argv) { CheckPs5DepthRegisterDecoding(); CheckDepthHtileStencilCompatibility(); CheckDepthAttachmentWrites(); + CheckDepthFeedbackAspects(); CheckDynamicRenderingState(); CheckDepthTargetFootprints(); CheckSlotVectorLifetime(); @@ -33334,6 +33541,7 @@ int main(int argc, char **argv) { return 2; } CheckShaderRecompilerFatalContracts(); + CheckDepthFeedbackAspects(); VulkanHarness vulkan; #endif CheckImageSamplerSpecialization();