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

View File

@ -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

View File

@ -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");
}
}

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);

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");

View File

@ -543,8 +543,15 @@ struct RenderExecutorTestAccess {
RenderColorInfo *colors,
uint32_t color_count,
RenderDepthInfo &depth,
const std::optional<PreparedBindings> &pixel = std::nullopt) {
return executor.AcquireRenderTargets(buffer, colors, color_count, depth, pixel);
std::span<PreparedBindings *const> 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<PreparedBindings *, 1> 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<PreparedBindings *, 1> 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<PreparedBindings *, 2> 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<PreparedBindings *, 2> 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<PreparedBindings *, 2> 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();