renderer_vulkan: Implement VK_KHR_dynamic_rendering

* Removes the need for framebuffers/renderpass on desktop
This commit is contained in:
GPUCode
2023-01-27 16:35:58 +02:00
parent a5f86e9813
commit 69b66cb41d
6 changed files with 167 additions and 24 deletions

View File

@ -529,7 +529,8 @@ bool Instance::CreateDevice() {
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT,
vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR, vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR,
vk::PhysicalDeviceCustomBorderColorFeaturesEXT, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT,
vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT>(); vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT,
vk::PhysicalDeviceDynamicRenderingFeaturesKHR>();
const vk::StructureChain properties_chain = const vk::StructureChain properties_chain =
physical_device.getProperties2<vk::PhysicalDeviceProperties2, physical_device.getProperties2<vk::PhysicalDeviceProperties2,
vk::PhysicalDevicePortabilitySubsetPropertiesKHR>(); vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
@ -568,6 +569,7 @@ bool Instance::CreateDevice() {
image_format_list = AddExtension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); image_format_list = AddExtension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
pipeline_creation_feedback = AddExtension(VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME); pipeline_creation_feedback = AddExtension(VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
bool has_portability_subset = AddExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); bool has_portability_subset = AddExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
bool has_dynamic_rendering = AddExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
bool has_extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); bool has_extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
bool has_extended_dynamic_state2 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); bool has_extended_dynamic_state2 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
bool has_extended_dynamic_state3 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); bool has_extended_dynamic_state3 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
@ -656,6 +658,7 @@ bool Instance::CreateDevice() {
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{},
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{},
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{}, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{},
vk::PhysicalDeviceDynamicRenderingFeaturesKHR{},
vk::PhysicalDeviceCustomBorderColorFeaturesEXT{}, vk::PhysicalDeviceCustomBorderColorFeaturesEXT{},
vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{}, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{},
vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT{}, vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT{},
@ -714,6 +717,13 @@ bool Instance::CreateDevice() {
device_chain.unlink<vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT>(); device_chain.unlink<vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT>();
} }
if (has_dynamic_rendering) {
FEAT_SET(vk::PhysicalDeviceDynamicRenderingFeaturesKHR, dynamicRendering,
dynamic_rendering);
} else {
device_chain.unlink<vk::PhysicalDeviceDynamicRenderingFeaturesKHR>();
}
if (has_custom_border_color) { if (has_custom_border_color) {
FEAT_SET(vk::PhysicalDeviceCustomBorderColorFeaturesEXT, customBorderColors, FEAT_SET(vk::PhysicalDeviceCustomBorderColorFeaturesEXT, customBorderColors,
custom_border_color) custom_border_color)

View File

@ -148,6 +148,11 @@ public:
return extended_dynamic_state3_color_write_mask; return extended_dynamic_state3_color_write_mask;
} }
/// Returns true when VK_KHR_dynamic_rendering is supported
bool IsDynamicRenderingSupported() const {
return dynamic_rendering;
}
/// Returns true when VK_KHR_push_descriptors is supported /// Returns true when VK_KHR_push_descriptors is supported
bool IsPushDescriptorsSupported() const { bool IsPushDescriptorsSupported() const {
return push_descriptors; return push_descriptors;
@ -305,6 +310,7 @@ private:
bool extended_dynamic_state3_color_blend_eq{}; bool extended_dynamic_state3_color_blend_eq{};
bool extended_dynamic_state3_color_write_mask{}; bool extended_dynamic_state3_color_write_mask{};
bool push_descriptors{}; bool push_descriptors{};
bool dynamic_rendering{};
bool custom_border_color{}; bool custom_border_color{};
bool index_type_uint8{}; bool index_type_uint8{};
bool image_format_list{}; bool image_format_list{};

View File

@ -110,10 +110,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline(
const Instance& instance_, RenderpassCache& renderpass_cache_, const PipelineInfo& info_, const Instance& instance_, RenderpassCache& renderpass_cache_, const PipelineInfo& info_,
vk::PipelineCache pipeline_cache_, vk::PipelineLayout layout_, std::array<Shader*, 3> stages_, vk::PipelineCache pipeline_cache_, vk::PipelineLayout layout_, std::array<Shader*, 3> stages_,
Common::ThreadWorker* worker_) Common::ThreadWorker* worker_)
: instance{instance_}, worker{worker_}, pipeline_layout{layout_}, : instance{instance_}, renderpass_cache{renderpass_cache_}, worker{worker_},
pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_}, pipeline_layout{layout_}, pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_} {
renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format,
info.attachments.depth_format, false)} {
// Ask the driver if it can give us the pipeline quickly // Ask the driver if it can give us the pipeline quickly
if (Build(true)) { if (Build(true)) {
@ -322,12 +320,6 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
.flags = vk::PipelineCreationFeedbackFlagBits::eValid, .flags = vk::PipelineCreationFeedbackFlagBits::eValid,
}; };
const vk::PipelineCreationFeedbackCreateInfoEXT creation_feedback_info = {
.pPipelineCreationFeedback = &creation_feedback,
.pipelineStageCreationFeedbackCount = shader_count,
.pPipelineStageCreationFeedbacks = creation_stage_feedback.data(),
};
vk::GraphicsPipelineCreateInfo pipeline_info = { vk::GraphicsPipelineCreateInfo pipeline_info = {
.stageCount = shader_count, .stageCount = shader_count,
.pStages = shader_stages.data(), .pStages = shader_stages.data(),
@ -340,17 +332,48 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
.pColorBlendState = &color_blending, .pColorBlendState = &color_blending,
.pDynamicState = &dynamic_info, .pDynamicState = &dynamic_info,
.layout = pipeline_layout, .layout = pipeline_layout,
.renderPass = renderpass,
}; };
if (!instance.IsDynamicRenderingSupported()) {
pipeline_info.renderPass = renderpass_cache.GetRenderpass(
info.attachments.color_format, info.attachments.depth_format, false);
}
if (fail_on_compile_required) { if (fail_on_compile_required) {
pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT; pipeline_info.flags |= vk::PipelineCreateFlagBits::eFailOnPipelineCompileRequiredEXT;
} }
if (instance.IsPipelineCreationFeedbackSupported()) {
pipeline_info.pNext = &creation_feedback_info; const auto [color, depth] = info.attachments;
const auto& color_traits = instance.GetTraits(color);
const auto& depth_traits = instance.GetTraits(depth);
const u32 color_attachment_count = color != VideoCore::PixelFormat::Invalid ? 1u : 0u;
vk::StructureChain pipeline_chain = {
pipeline_info,
vk::PipelineCreationFeedbackCreateInfoEXT{
.pPipelineCreationFeedback = &creation_feedback,
.pipelineStageCreationFeedbackCount = shader_count,
.pPipelineStageCreationFeedbacks = creation_stage_feedback.data(),
},
vk::PipelineRenderingCreateInfoKHR{
.colorAttachmentCount = color_attachment_count,
.pColorAttachmentFormats = &color_traits.native,
.depthAttachmentFormat = depth_traits.native,
.stencilAttachmentFormat = depth_traits.aspect & vk::ImageAspectFlagBits::eStencil
? depth_traits.native
: vk::Format::eUndefined,
},
};
if (!instance.IsPipelineCreationFeedbackSupported()) {
pipeline_chain.unlink<vk::PipelineCreationFeedbackCreateInfoEXT>();
}
if (!instance.IsDynamicRenderingSupported()) {
pipeline_chain.unlink<vk::PipelineRenderingCreateInfoKHR>();
} }
const vk::ResultValue result = device.createGraphicsPipeline(pipeline_cache, pipeline_info); const vk::ResultValue result =
device.createGraphicsPipeline(pipeline_cache, pipeline_chain.get());
if (result.result == vk::Result::eSuccess) { if (result.result == vk::Result::eSuccess) {
pipeline = result.value; pipeline = result.value;
} else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) { } else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) {

View File

@ -162,6 +162,7 @@ class PipelineCache {
private: private:
const Instance& instance; const Instance& instance;
RenderpassCache& renderpass_cache;
Common::ThreadWorker* worker; Common::ThreadWorker* worker;
vk::Pipeline pipeline; vk::Pipeline pipeline;
@ -170,7 +171,6 @@ class PipelineCache {
PipelineInfo info; PipelineInfo info;
std::array<Shader*, 3> stages; std::array<Shader*, 3> stages;
vk::RenderPass renderpass;
}; };
public: public:

View File

@ -12,7 +12,8 @@
namespace Vulkan { namespace Vulkan {
RenderpassCache::RenderpassCache(const Instance& instance, Scheduler& scheduler) RenderpassCache::RenderpassCache(const Instance& instance, Scheduler& scheduler)
: instance{instance}, scheduler{scheduler} {} : instance{instance}, scheduler{scheduler}, dynamic_rendering{
instance.IsDynamicRenderingSupported()} {}
RenderpassCache::~RenderpassCache() { RenderpassCache::~RenderpassCache() {
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
@ -39,6 +40,10 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) { vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
ASSERT(color || depth_stencil); ASSERT(color || depth_stencil);
if (dynamic_rendering) {
return BeginRendering(color, depth_stencil, render_area, do_clear, clear);
}
u32 width = UINT32_MAX; u32 width = UINT32_MAX;
u32 height = UINT32_MAX; u32 height = UINT32_MAX;
u32 cursor = 0; u32 cursor = 0;
@ -81,13 +86,14 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
.clear = clear, .clear = clear,
}; };
const u64 new_state_hash = Common::ComputeStructHash64(new_state);
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass); const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
if (current_state == new_state && !is_dirty) { if (state_hash == new_state_hash && rendering && !is_dirty) {
cmd_count++; cmd_count++;
return; return;
} }
if (current_state.renderpass) { if (rendering) {
ExitRenderpass(); ExitRenderpass();
} }
@ -104,16 +110,23 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
}); });
scheduler.MarkStateNonDirty(StateFlags::Renderpass); scheduler.MarkStateNonDirty(StateFlags::Renderpass);
current_state = new_state; state_hash = new_state_hash;
rendering = true;
} }
void RenderpassCache::ExitRenderpass() { void RenderpassCache::ExitRenderpass() {
if (!current_state.renderpass) { if (!rendering) {
return; return;
} }
scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endRenderPass(); }); rendering = false;
current_state = {}; scheduler.Record([dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) {
if (dynamic_rendering) {
cmdbuf.endRenderingKHR();
} else {
cmdbuf.endRenderPass();
}
});
// The Mali guide recommends flushing at the end of each major renderpass // The Mali guide recommends flushing at the end of each major renderpass
// Testing has shown this has a significant effect on rendering performance // Testing has shown this has a significant effect on rendering performance
@ -123,6 +136,78 @@ void RenderpassCache::ExitRenderpass() {
} }
} }
void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
RenderingState new_state = {
.render_area = render_area,
.clear = clear,
.do_clear = do_clear,
};
if (color) {
new_state.color_view = color->GetFramebufferView();
}
if (depth_stencil) {
new_state.depth_view = depth_stencil->GetFramebufferView();
}
const u64 new_state_hash = Common::ComputeStructHash64(new_state);
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
if (state_hash == new_state_hash && rendering && !is_dirty) {
cmd_count++;
return;
}
if (rendering) {
ExitRenderpass();
}
const bool has_stencil =
depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil;
scheduler.Record([new_state, has_stencil](vk::CommandBuffer cmdbuf) {
u32 cursor = 0;
std::array<vk::RenderingAttachmentInfoKHR, 2> infos{};
const auto Prepare = [&](vk::ImageView image_view) {
if (!image_view) {
cursor++;
return;
}
infos[cursor++] = vk::RenderingAttachmentInfoKHR{
.imageView = image_view,
.imageLayout = vk::ImageLayout::eGeneral,
.loadOp =
new_state.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
.storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = new_state.clear,
};
};
Prepare(new_state.color_view);
Prepare(new_state.depth_view);
const u32 color_attachment_count = new_state.color_view ? 1u : 0u;
const vk::RenderingAttachmentInfoKHR* depth_info =
new_state.depth_view ? &infos[1] : nullptr;
const vk::RenderingAttachmentInfoKHR* stencil_info = has_stencil ? &infos[1] : nullptr;
const vk::RenderingInfoKHR rendering_info = {
.renderArea = new_state.render_area,
.layerCount = 1,
.colorAttachmentCount = color_attachment_count,
.pColorAttachments = &infos[0],
.pDepthAttachment = depth_info,
.pStencilAttachment = stencil_info,
};
cmdbuf.beginRenderingKHR(rendering_info);
});
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
state_hash = new_state_hash;
rendering = true;
}
void RenderpassCache::CreatePresentRenderpass(vk::Format format) { void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
if (!present_renderpass) { if (!present_renderpass) {
present_renderpass = present_renderpass =

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <cstring> #include <cstring>
#include <variant>
#include "common/hash.h" #include "common/hash.h"
#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
@ -65,6 +66,10 @@ public:
} }
private: private:
/// Begins a new rendering scope using dynamic rendering
void BeginRendering(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area,
bool do_clear, vk::ClearValue clear);
/// Creates a renderpass configured appropriately and stores it in cached_renderpasses /// Creates a renderpass configured appropriately and stores it in cached_renderpasses
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth, vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth,
vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout, vk::AttachmentLoadOp load_op, vk::ImageLayout initial_layout,
@ -85,13 +90,27 @@ private:
} }
}; };
struct RenderingState {
vk::ImageView color_view;
vk::ImageView depth_view;
vk::Rect2D render_area;
vk::ClearValue clear;
bool do_clear;
[[nodiscard]] bool operator==(const RenderpassState& other) const {
return std::memcmp(this, &other, sizeof(RenderpassState)) == 0;
}
};
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;
RenderpassState current_state{};
vk::RenderPass present_renderpass{}; vk::RenderPass present_renderpass{};
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2]; vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers; std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
bool rendering = false;
bool dynamic_rendering = false;
u32 cmd_count{}; u32 cmd_count{};
u64 state_hash{};
}; };
} // namespace Vulkan } // namespace Vulkan