renderer_vulkan: Implement VK_KHR_dynamic_rendering
* Removes the need for framebuffers/renderpass on desktop
This commit is contained in:
@ -529,7 +529,8 @@ bool Instance::CreateDevice() {
|
||||
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT,
|
||||
vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR,
|
||||
vk::PhysicalDeviceCustomBorderColorFeaturesEXT, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT,
|
||||
vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT>();
|
||||
vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT,
|
||||
vk::PhysicalDeviceDynamicRenderingFeaturesKHR>();
|
||||
const vk::StructureChain properties_chain =
|
||||
physical_device.getProperties2<vk::PhysicalDeviceProperties2,
|
||||
vk::PhysicalDevicePortabilitySubsetPropertiesKHR>();
|
||||
@ -568,6 +569,7 @@ bool Instance::CreateDevice() {
|
||||
image_format_list = AddExtension(VK_KHR_IMAGE_FORMAT_LIST_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_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_state2 = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_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::PhysicalDeviceExtendedDynamicState2FeaturesEXT{},
|
||||
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{},
|
||||
vk::PhysicalDeviceDynamicRenderingFeaturesKHR{},
|
||||
vk::PhysicalDeviceCustomBorderColorFeaturesEXT{},
|
||||
vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{},
|
||||
vk::PhysicalDevicePipelineCreationCacheControlFeaturesEXT{},
|
||||
@ -714,6 +717,13 @@ bool Instance::CreateDevice() {
|
||||
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) {
|
||||
FEAT_SET(vk::PhysicalDeviceCustomBorderColorFeaturesEXT, customBorderColors,
|
||||
custom_border_color)
|
||||
|
@ -148,6 +148,11 @@ public:
|
||||
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
|
||||
bool IsPushDescriptorsSupported() const {
|
||||
return push_descriptors;
|
||||
@ -305,6 +310,7 @@ private:
|
||||
bool extended_dynamic_state3_color_blend_eq{};
|
||||
bool extended_dynamic_state3_color_write_mask{};
|
||||
bool push_descriptors{};
|
||||
bool dynamic_rendering{};
|
||||
bool custom_border_color{};
|
||||
bool index_type_uint8{};
|
||||
bool image_format_list{};
|
||||
|
@ -110,10 +110,8 @@ PipelineCache::GraphicsPipeline::GraphicsPipeline(
|
||||
const Instance& instance_, RenderpassCache& renderpass_cache_, const PipelineInfo& info_,
|
||||
vk::PipelineCache pipeline_cache_, vk::PipelineLayout layout_, std::array<Shader*, 3> stages_,
|
||||
Common::ThreadWorker* worker_)
|
||||
: instance{instance_}, worker{worker_}, pipeline_layout{layout_},
|
||||
pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_},
|
||||
renderpass{renderpass_cache_.GetRenderpass(info.attachments.color_format,
|
||||
info.attachments.depth_format, false)} {
|
||||
: instance{instance_}, renderpass_cache{renderpass_cache_}, worker{worker_},
|
||||
pipeline_layout{layout_}, pipeline_cache{pipeline_cache_}, info{info_}, stages{stages_} {
|
||||
|
||||
// Ask the driver if it can give us the pipeline quickly
|
||||
if (Build(true)) {
|
||||
@ -322,12 +320,6 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
|
||||
.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 = {
|
||||
.stageCount = shader_count,
|
||||
.pStages = shader_stages.data(),
|
||||
@ -340,17 +332,48 @@ bool PipelineCache::GraphicsPipeline::Build(bool fail_on_compile_required) {
|
||||
.pColorBlendState = &color_blending,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.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) {
|
||||
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) {
|
||||
pipeline = result.value;
|
||||
} else if (result.result == vk::Result::eErrorPipelineCompileRequiredEXT) {
|
||||
|
@ -162,6 +162,7 @@ class PipelineCache {
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
RenderpassCache& renderpass_cache;
|
||||
Common::ThreadWorker* worker;
|
||||
|
||||
vk::Pipeline pipeline;
|
||||
@ -170,7 +171,6 @@ class PipelineCache {
|
||||
|
||||
PipelineInfo info;
|
||||
std::array<Shader*, 3> stages;
|
||||
vk::RenderPass renderpass;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -12,7 +12,8 @@
|
||||
namespace Vulkan {
|
||||
|
||||
RenderpassCache::RenderpassCache(const Instance& instance, Scheduler& scheduler)
|
||||
: instance{instance}, scheduler{scheduler} {}
|
||||
: instance{instance}, scheduler{scheduler}, dynamic_rendering{
|
||||
instance.IsDynamicRenderingSupported()} {}
|
||||
|
||||
RenderpassCache::~RenderpassCache() {
|
||||
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) {
|
||||
ASSERT(color || depth_stencil);
|
||||
|
||||
if (dynamic_rendering) {
|
||||
return BeginRendering(color, depth_stencil, render_area, do_clear, clear);
|
||||
}
|
||||
|
||||
u32 width = UINT32_MAX;
|
||||
u32 height = UINT32_MAX;
|
||||
u32 cursor = 0;
|
||||
@ -81,13 +86,14 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
|
||||
.clear = clear,
|
||||
};
|
||||
|
||||
const u64 new_state_hash = Common::ComputeStructHash64(new_state);
|
||||
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++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_state.renderpass) {
|
||||
if (rendering) {
|
||||
ExitRenderpass();
|
||||
}
|
||||
|
||||
@ -104,16 +110,23 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
|
||||
});
|
||||
|
||||
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
|
||||
current_state = new_state;
|
||||
state_hash = new_state_hash;
|
||||
rendering = true;
|
||||
}
|
||||
|
||||
void RenderpassCache::ExitRenderpass() {
|
||||
if (!current_state.renderpass) {
|
||||
if (!rendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endRenderPass(); });
|
||||
current_state = {};
|
||||
rendering = false;
|
||||
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
|
||||
// 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) {
|
||||
if (!present_renderpass) {
|
||||
present_renderpass =
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <variant>
|
||||
#include "common/hash.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
@ -65,6 +66,10 @@ public:
|
||||
}
|
||||
|
||||
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
|
||||
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth,
|
||||
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;
|
||||
Scheduler& scheduler;
|
||||
RenderpassState current_state{};
|
||||
vk::RenderPass present_renderpass{};
|
||||
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
|
||||
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
|
||||
bool rendering = false;
|
||||
bool dynamic_rendering = false;
|
||||
u32 cmd_count{};
|
||||
u64 state_hash{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Reference in New Issue
Block a user