renderer_vulkan: Minimize state changes

* Store current renderpass/pipelines and only rebind when they change

* Enable extended dynamic state support and only apply them when they change
This commit is contained in:
GPUCode
2022-09-25 21:25:30 +03:00
parent 6e1bfe9949
commit ebd23026a0
8 changed files with 102 additions and 57 deletions

View File

@ -953,9 +953,9 @@ void RasterizerCache<T>::DownloadSurface(const Surface& surface, SurfaceInterval
if (surface->is_tiled) {
std::vector<std::byte> swizzled_data(staging.size);
runtime.FormatConvert(surface->pixel_format, false, swizzled_data, swizzled_data);
SwizzleTexture(*surface, flush_start - surface->addr, flush_end - surface->addr,
staging.mapped, swizzled_data);
runtime.FormatConvert(surface->pixel_format, false, swizzled_data, download_dest);
staging.mapped, download_dest);
} else {
runtime.FormatConvert(surface->pixel_format, false, staging.mapped, download_dest);
}

View File

@ -1044,6 +1044,13 @@ void RendererVulkan::SwapBuffers() {
// Inform texture runtime about the switch
runtime.OnSlotSwitch(scheduler.GetCurrentSlotIndex());
// When the command buffer switches, all state becomes undefined.
// This is problematic when using dynamic states, so set all
// states here
if (instance.IsExtendedDynamicStateSupported()) {
rasterizer->SyncFixedState();
}
}
} // namespace Vulkan

View File

@ -74,8 +74,7 @@ public:
/// Returns true when VK_EXT_extended_dynamic_state is supported
bool IsExtendedDynamicStateSupported() const {
// TODO: Enable this when the pipeline builder is confirmed functional
return false;
return extended_dynamic_state;
}
/// Returns true when VK_KHR_push_descriptors is supported

View File

@ -197,8 +197,11 @@ void PipelineCache::BindPipeline(const PipelineInfo& info) {
it->second = BuildPipeline(info);
}
if (it->second != current_pipeline) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, it->second);
current_pipeline = it->second;
}
BindDescriptorSets();
}
@ -302,27 +305,13 @@ void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) {
void PipelineCache::MarkDescriptorSetsDirty() {
descriptor_dirty.fill(true);
current_pipeline = VK_NULL_HANDLE;
}
void PipelineCache::ApplyDynamic(const PipelineInfo& info) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_compare_mask);
command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_write_mask);
command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_reference);
if (instance.IsExtendedDynamicStateSupported()) {
command_buffer.setCullModeEXT(PicaToVK::CullMode(info.rasterization.cull_mode));
command_buffer.setDepthCompareOpEXT(PicaToVK::CompareFunc(info.depth_stencil.depth_compare_op));
command_buffer.setDepthTestEnableEXT(info.depth_stencil.depth_test_enable);
command_buffer.setDepthWriteEnableEXT(info.depth_stencil.depth_write_enable);
command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(info.rasterization.cull_mode));
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setPrimitiveTopologyEXT(PicaToVK::PrimitiveTopology(info.rasterization.topology));
command_buffer.setStencilTestEnableEXT(info.depth_stencil.stencil_test_enable);
command_buffer.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack,
PicaToVK::StencilOp(info.depth_stencil.stencil_fail_op),
PicaToVK::StencilOp(info.depth_stencil.stencil_pass_op),
PicaToVK::StencilOp(info.depth_stencil.stencil_depth_fail_op),
PicaToVK::CompareFunc(info.depth_stencil.stencil_compare_op));
}
}
@ -505,7 +494,6 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) {
const std::array dynamic_states = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
vk::DynamicState::eLineWidth,
vk::DynamicState::eStencilCompareMask,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference,

View File

@ -172,6 +172,7 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan
}
RasterizerVulkan::~RasterizerVulkan() {
renderpass_cache.ExitRenderpass();
scheduler.Submit(SubmitMode::Flush | SubmitMode::Shutdown);
VmaAllocator allocator = instance.GetAllocator();
@ -197,17 +198,7 @@ void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading,
void RasterizerVulkan::SyncEntireState() {
// Sync fixed function Vulkan state
SyncClipEnabled();
SyncCullMode();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
SyncLogicOp();
SyncStencilTest();
SyncDepthTest();
SyncColorWriteMask();
SyncStencilWriteMask();
SyncDepthWriteMask();
SyncFixedState();
// Sync uniforms
SyncClipCoef();
@ -237,6 +228,20 @@ void RasterizerVulkan::SyncEntireState() {
SyncShadowTextureBias();
}
void RasterizerVulkan::SyncFixedState() {
SyncClipEnabled();
SyncCullMode();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
SyncLogicOp();
SyncStencilTest();
SyncDepthTest();
SyncColorWriteMask();
SyncStencilWriteMask();
SyncDepthWriteMask();
}
/**
* This is a helper function to resolve an issue when interpolating opposite quaternions. See below
* for a detailed description of this issue (yuriks):
@ -1641,6 +1646,12 @@ void RasterizerVulkan::SyncClipCoef() {
void RasterizerVulkan::SyncCullMode() {
const auto& regs = Pica::g_state.regs;
if (instance.IsExtendedDynamicStateSupported()) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setCullModeEXT(PicaToVK::CullMode(regs.rasterizer.cull_mode));
command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(regs.rasterizer.cull_mode));
}
pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode);
}
@ -1748,36 +1759,71 @@ void RasterizerVulkan::SyncStencilWriteMask() {
(regs.framebuffer.framebuffer.allow_depth_stencil_write != 0)
? static_cast<u32>(regs.framebuffer.output_merger.stencil_test.write_mask)
: 0;
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, pipeline_info.depth_stencil.stencil_write_mask);
}
void RasterizerVulkan::SyncDepthWriteMask() {
const auto& regs = Pica::g_state.regs;
pipeline_info.depth_stencil.depth_write_enable.Assign(
(regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 &&
regs.framebuffer.output_merger.depth_write_enable));
const bool write_enable = (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 &&
regs.framebuffer.output_merger.depth_write_enable);
if (instance.IsExtendedDynamicStateSupported()) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setDepthWriteEnableEXT(write_enable);
}
pipeline_info.depth_stencil.depth_write_enable.Assign(write_enable);
}
void RasterizerVulkan::SyncStencilTest() {
const auto& regs = Pica::g_state.regs;
pipeline_info.depth_stencil.stencil_test_enable.Assign(regs.framebuffer.output_merger.stencil_test.enable &&
regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8);
pipeline_info.depth_stencil.stencil_fail_op.Assign(regs.framebuffer.output_merger.stencil_test.action_stencil_fail);
pipeline_info.depth_stencil.stencil_pass_op.Assign(regs.framebuffer.output_merger.stencil_test.action_depth_pass);
pipeline_info.depth_stencil.stencil_depth_fail_op.Assign(regs.framebuffer.output_merger.stencil_test.action_depth_fail);
pipeline_info.depth_stencil.stencil_compare_op.Assign(regs.framebuffer.output_merger.stencil_test.func);
pipeline_info.depth_stencil.stencil_reference = regs.framebuffer.output_merger.stencil_test.reference_value;
pipeline_info.depth_stencil.stencil_write_mask = regs.framebuffer.output_merger.stencil_test.input_mask;
const bool test_enable = regs.framebuffer.output_merger.stencil_test.enable &&
regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
const auto& stencil_test = regs.framebuffer.output_merger.stencil_test;
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.input_mask);
command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.reference_value);
if (instance.IsExtendedDynamicStateSupported()) {
command_buffer.setStencilTestEnableEXT(test_enable);
command_buffer.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack,
PicaToVK::StencilOp(stencil_test.action_stencil_fail),
PicaToVK::StencilOp(stencil_test.action_depth_pass),
PicaToVK::StencilOp(stencil_test.action_depth_fail),
PicaToVK::CompareFunc(stencil_test.func));
}
pipeline_info.depth_stencil.stencil_test_enable.Assign(test_enable);
pipeline_info.depth_stencil.stencil_fail_op.Assign(stencil_test.action_stencil_fail);
pipeline_info.depth_stencil.stencil_pass_op.Assign(stencil_test.action_depth_pass);
pipeline_info.depth_stencil.stencil_depth_fail_op.Assign(stencil_test.action_depth_fail);
pipeline_info.depth_stencil.stencil_compare_op.Assign(stencil_test.func);
pipeline_info.depth_stencil.stencil_reference = stencil_test.reference_value;
pipeline_info.depth_stencil.stencil_write_mask = stencil_test.input_mask;
}
void RasterizerVulkan::SyncDepthTest() {
const auto& regs = Pica::g_state.regs;
pipeline_info.depth_stencil.depth_test_enable.Assign(regs.framebuffer.output_merger.depth_test_enable == 1 ||
regs.framebuffer.output_merger.depth_write_enable == 1);
pipeline_info.depth_stencil.depth_compare_op.Assign(
regs.framebuffer.output_merger.depth_test_enable == 1
const bool test_enabled = regs.framebuffer.output_merger.depth_test_enable == 1 ||
regs.framebuffer.output_merger.depth_write_enable == 1;
const auto compare_op = regs.framebuffer.output_merger.depth_test_enable == 1
? regs.framebuffer.output_merger.depth_test_func.Value()
: Pica::FramebufferRegs::CompareFunc::Always);
: Pica::FramebufferRegs::CompareFunc::Always;
if (instance.IsExtendedDynamicStateSupported()) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setDepthCompareOpEXT(PicaToVK::CompareFunc(compare_op));
command_buffer.setDepthTestEnableEXT(test_enabled);
}
pipeline_info.depth_stencil.depth_test_enable.Assign(test_enabled);
pipeline_info.depth_stencil.depth_compare_op.Assign(compare_op);
}
void RasterizerVulkan::SyncCombinerColor() {

View File

@ -103,6 +103,9 @@ public:
/// Syncs entire status to match PICA registers
void SyncEntireState() override;
/// Sync fixed function pipeline state
void SyncFixedState();
/// Flushes all rasterizer owned buffers
void FlushBuffers();

View File

@ -7,7 +7,6 @@
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
namespace Vulkan {
@ -77,24 +76,27 @@ RenderpassCache::~RenderpassCache() {
}
void RenderpassCache::EnterRenderpass(const vk::RenderPassBeginInfo begin_info) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
if (active_renderpass == begin_info.renderPass) {
return;
}
if (renderpass_active) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
if (active_renderpass) {
command_buffer.endRenderPass();
}
command_buffer.beginRenderPass(begin_info, vk::SubpassContents::eInline);
renderpass_active = true;
active_renderpass = begin_info.renderPass;
}
void RenderpassCache::ExitRenderpass() {
if (!renderpass_active) {
if (!active_renderpass) {
return;
}
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.endRenderPass();
renderpass_active = false;
active_renderpass = VK_NULL_HANDLE;
}
void RenderpassCache::CreatePresentRenderpass(vk::Format format) {

View File

@ -47,7 +47,7 @@ private:
const Instance& instance;
TaskScheduler& scheduler;
bool renderpass_active = false;
vk::RenderPass active_renderpass = VK_NULL_HANDLE;
vk::RenderPass present_renderpass{};
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS+1][MAX_DEPTH_FORMATS+1][2];
};