render: support overlapping fragment depth feedback

This commit is contained in:
nmzik
2026-09-07 11:10:04 +04:00
parent 587df9145f
commit 2097ebb414
8 changed files with 286 additions and 84 deletions
+1
View File
@@ -32,6 +32,7 @@ struct GraphicContext {
bool rt_extensions_enabled = false;
bool compute_subgroup_size_control_enabled = false;
bool sample_rate_shading_enabled = false;
bool attachment_feedback_loop_enabled = false;
bool mesh_shader_enabled = false;
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
uint32_t subgroup_size = 0;
@@ -54,6 +54,9 @@ namespace {
}
if (DepthAspectTransferFormat(info.pixel_format) != vk::Format::eUndefined) {
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
if (graphics.attachment_feedback_loop_enabled && (usage & vk::ImageUsageFlagBits::eSampled)) {
usage |= vk::ImageUsageFlagBits::eAttachmentFeedbackLoopEXT;
}
return usage;
}
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
@@ -1004,8 +1004,11 @@ 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 =
layout == vk::ImageLayout::eDepthReadOnlyOptimal ||
depth_feedback || layout == vk::ImageLayout::eDepthReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthStencilReadOnlyOptimal ||
layout == vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal;
const bool stencil_read =
@@ -702,7 +702,7 @@ void CreatePipelineInternal(
depth_stencil_info.minDepthBounds = static_params.depth_min_bounds;
depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds;
const vk::DynamicState dynamic_states[] = {
std::vector<vk::DynamicState> dynamic_states {
vk::DynamicState::eViewportWithCount,
vk::DynamicState::eScissorWithCount,
vk::DynamicState::eLineWidth,
@@ -715,22 +715,19 @@ void CreatePipelineInternal(
vk::DynamicState::eStencilReference,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eBlendConstants,
#if !defined(__APPLE__)
// Keep last so depth-only pipelines can omit this dynamic state.
vk::DynamicState::eColorWriteEnableEXT, // unsupported by MoltenVK; static mask instead
#endif
};
auto dynamic_states_count =
static_cast<uint32_t>(sizeof(dynamic_states) / sizeof(dynamic_states[0]));
#if !defined(__APPLE__)
if (rendering.color_count == 0) {
dynamic_states_count--;
if (rendering.color_count != 0) {
dynamic_states.push_back(vk::DynamicState::eColorWriteEnableEXT);
}
#endif
if (graphics.attachment_feedback_loop_enabled) {
dynamic_states.push_back(vk::DynamicState::eAttachmentFeedbackLoopEnableEXT);
}
vk::PipelineDynamicStateCreateInfo dynamic_state {};
dynamic_state.dynamicStateCount = dynamic_states_count;
dynamic_state.pDynamicStates = dynamic_states;
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
dynamic_state.pDynamicStates = dynamic_states.data();
vk::GraphicsPipelineCreateInfo pipeline_info {};
vk::PipelineRenderingCreateInfo rendering_info {};
@@ -764,7 +761,7 @@ void CreatePipelineInternal(
vertex_program.id, ps_active ? pixel_program.id : 0,
static_cast<uint32_t>(static_params.topology), static_params.color_mask[0],
(with_depth ? "true" : "false"), (static_params.blend_enable[0] ? "true" : "false"),
dynamic_states_count);
dynamic_state.dynamicStateCount);
}
result = graphics.device.createGraphicsPipelines(driver_cache, 1, &pipeline_info, nullptr,
&pipeline.pipeline);
+2 -1
View File
@@ -195,7 +195,8 @@ private:
bool primitive_restart_enable, bool log_pipeline_phase,
bool set_bind_debug, bool set_auto_debug);
[[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
uint32_t color_count, RenderDepthInfo& depth);
uint32_t color_count, RenderDepthInfo& depth,
const std::optional<PreparedBindings>& pixel = std::nullopt);
[[nodiscard]] bool ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
uint32_t render_target_slice_offset);
void BindImage(ImageId id, bool storage);
+33 -3
View File
@@ -475,7 +475,8 @@ struct DrawCallInfo {
};
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
uint32_t color_count, RenderDepthInfo& depth) {
uint32_t color_count, RenderDepthInfo& depth,
const std::optional<PreparedBindings>& pixel) {
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
auto& cache = m_context.GetTextureCache();
RenderState state {};
@@ -567,7 +568,28 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
depth.desc.info.samples);
}
const auto layout = depth_attachment_layout(depth);
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) {
EXIT("depth attachment feedback loop is not supported by the host\n");
}
const auto layout = feedback ? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
: depth_attachment_layout(depth);
// The attachment store writes even when guest depth/stencil tests do not.
const auto access = vk::AccessFlagBits2::eDepthStencilAttachmentRead |
vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
@@ -1089,7 +1111,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
index_binding = PrepareIndexBuffer(buffer, index_source);
}
const auto rendering =
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info);
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info,
bindings.pixel);
if (log_pipeline_phase) {
LogDrawPhase(draw.name, "CreatePipeline");
@@ -1142,6 +1165,13 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
SetGraphicsDynamicParams(buffer, vk_buffer, state.vs_input_info, state.color_info,
state.color_count, state.depth_info);
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 {});
}
LogDrawPhase(draw.name, "BeginRendering");
if (set_auto_debug) {
@@ -623,7 +623,22 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
supported_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
supported_features2.pNext = mesh_extension ? static_cast<void*>(&supported_mesh)
: static_cast<void*>(&supported_features13);
const bool feedback_extensions =
HasExtension(device_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME) &&
HasExtension(device_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME);
vk::PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT feedback_layout {};
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT feedback_dynamic {};
if (feedback_extensions) {
feedback_dynamic.pNext = supported_features2.pNext;
feedback_layout.pNext = &feedback_dynamic;
supported_features2.pNext = &feedback_layout;
}
physical_device.getFeatures2(&supported_features2);
graphics.attachment_feedback_loop_enabled =
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
feedback_dynamic.attachmentFeedbackLoopDynamicState;
LOGF("Vulkan depth feedback support: %s\n",
graphics.attachment_feedback_loop_enabled ? "true" : "false");
graphics.mesh_shader_enabled = mesh_extension && supported_mesh.meshShader;
if (graphics.mesh_shader_enabled) {
vk::PhysicalDeviceProperties2 properties {};
@@ -729,8 +744,11 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
mesh_features.pNext = &features13;
mesh_features.meshShader = graphics.mesh_shader_enabled;
create_info.sType = vk::StructureType::eDeviceCreateInfo;
create_info.pNext =
feedback_dynamic.pNext =
mesh_extension ? static_cast<void*>(&mesh_features) : static_cast<void*>(&features13);
create_info.pNext = graphics.attachment_feedback_loop_enabled
? static_cast<void*>(&feedback_layout)
: feedback_dynamic.pNext;
create_info.flags = {};
create_info.pQueueCreateInfos = &queue_create_info;
create_info.queueCreateInfoCount = 1;
@@ -1117,6 +1135,11 @@ void WindowContext::CreateVulkan() {
device_extensions.push_back(extension);
}
}
if (HasExtension(available_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME) &&
HasExtension(available_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME)) {
device_extensions.push_back(VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME);
device_extensions.push_back(VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME);
}
}
VulkanInitSubgroupSizeControl(graphic_ctx.physical_device, graphic_ctx);