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:
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
};
|
||||
|
Reference in New Issue
Block a user