renderer_vulkan: Actually minize state changes

* Keep track of the current state and only update it when needed. Previously games would set the same state over and over cluttering renderdoc logs
This commit is contained in:
emufan4568
2022-10-12 17:18:29 +03:00
committed by GPUCode
parent 2a68cab7d6
commit b4d0f442c8
4 changed files with 99 additions and 61 deletions

View File

@ -982,12 +982,6 @@ void RendererVulkan::FlushBuffers() {
}
void RendererVulkan::OnSlotSwitch() {
// When the command buffer switches, all state becomes undefined.
// This is problematic with dynamic states, so set all states here
if (instance.IsExtendedDynamicStateSupported()) {
rasterizer->SyncFixedState();
}
runtime.OnSlotSwitch(scheduler.GetCurrentSlotIndex());
renderpass_cache.OnSlotSwitch();
rasterizer->pipeline_cache.MarkDirty();

View File

@ -75,6 +75,8 @@ u32 AttribBytes(VertexAttribute attrib) {
case AttribType::Ubyte:
return sizeof(u8) * attrib.size;
}
return 0;
}
vk::Format ToVkAttributeFormat(VertexAttribute attrib) {
@ -264,26 +266,105 @@ void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) {
}
void PipelineCache::SetViewport(float x, float y, float width, float height) {
const vk::Viewport viewport{x, y, width, height, 0.f, 1.f};
if (viewport != current_viewport || state_dirty) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setViewport(0, vk::Viewport{x, y, width, height, 0.f, 1.f});
current_viewport = viewport;
}
}
void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) {
const vk::Rect2D scissor{{x, y}, {width, height}};
if (scissor != current_scissor || state_dirty) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.setScissor(0, vk::Rect2D{{x, y}, {width, height}});
current_scissor = scissor;
}
}
void PipelineCache::MarkDirty() {
descriptor_dirty.fill(true);
current_pipeline = VK_NULL_HANDLE;
state_dirty = true;
}
void PipelineCache::ApplyDynamic(const PipelineInfo& info) {
if (instance.IsExtendedDynamicStateSupported()) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
if (info.depth_stencil.stencil_compare_mask !=
current_info.depth_stencil.stencil_compare_mask ||
state_dirty) {
command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack,
info.depth_stencil.stencil_compare_mask);
}
if (info.depth_stencil.stencil_write_mask != current_info.depth_stencil.stencil_write_mask ||
state_dirty) {
command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack,
info.depth_stencil.stencil_write_mask);
}
if (info.depth_stencil.stencil_reference != current_info.depth_stencil.stencil_reference ||
state_dirty) {
command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack,
info.depth_stencil.stencil_reference);
}
if (instance.IsExtendedDynamicStateSupported()) {
if (info.rasterization.cull_mode != current_info.rasterization.cull_mode || state_dirty) {
command_buffer.setCullModeEXT(PicaToVK::CullMode(info.rasterization.cull_mode));
command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(info.rasterization.cull_mode));
}
if (info.depth_stencil.depth_compare_op != current_info.depth_stencil.depth_compare_op ||
state_dirty) {
command_buffer.setDepthCompareOpEXT(
PicaToVK::CompareFunc(info.depth_stencil.depth_compare_op));
}
if (info.depth_stencil.depth_test_enable != current_info.depth_stencil.depth_test_enable ||
state_dirty) {
command_buffer.setDepthTestEnableEXT(info.depth_stencil.depth_test_enable);
}
if (info.depth_stencil.depth_write_enable !=
current_info.depth_stencil.depth_write_enable ||
state_dirty) {
command_buffer.setDepthWriteEnableEXT(info.depth_stencil.depth_write_enable);
}
if (info.rasterization.topology != current_info.rasterization.topology || state_dirty) {
command_buffer.setPrimitiveTopologyEXT(
PicaToVK::PrimitiveTopology(info.rasterization.topology));
}
if (info.depth_stencil.stencil_test_enable !=
current_info.depth_stencil.stencil_test_enable ||
state_dirty) {
command_buffer.setStencilTestEnableEXT(info.depth_stencil.stencil_test_enable);
}
if (info.depth_stencil.stencil_fail_op != current_info.depth_stencil.stencil_fail_op ||
info.depth_stencil.stencil_pass_op != current_info.depth_stencil.stencil_pass_op ||
info.depth_stencil.stencil_depth_fail_op !=
current_info.depth_stencil.stencil_depth_fail_op ||
info.depth_stencil.stencil_compare_op !=
current_info.depth_stencil.stencil_compare_op ||
state_dirty) {
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));
}
}
current_info = info;
state_dirty = false;
}
void PipelineCache::SetBinding(u32 set, u32 binding, DescriptorData data) {
@ -539,12 +620,11 @@ void PipelineCache::LoadDiskCache() {
if (cache_file.IsOpen()) {
LOG_INFO(Render_Vulkan, "Loading pipeline cache");
const u32 cache_file_size = cache_file.GetSize();
const u64 cache_file_size = cache_file.GetSize();
cache_data.resize(cache_file_size);
if (cache_file.ReadBytes(cache_data.data(), cache_file_size)) {
if (!IsCacheValid(cache_data.data(), cache_file_size)) {
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, deleting");
FileUtil::Delete(cache_file_path);
LOG_WARNING(Render_Vulkan, "Pipeline cache provided invalid, ignoring");
} else {
cache_info.initialDataSize = cache_file_size;
cache_info.pInitialData = cache_data.data();

View File

@ -230,6 +230,9 @@ private:
vk::PipelineCache pipeline_cache;
std::unordered_map<u64, vk::Pipeline, Common::IdentityHash<u64>> graphics_pipelines;
vk::Pipeline current_pipeline{};
PipelineInfo current_info{};
vk::Viewport current_viewport{};
vk::Rect2D current_scissor{};
// Cached layouts for the rasterizer pipelines
vk::PipelineLayout layout;
@ -240,6 +243,7 @@ private:
std::array<DescriptorSetData, MAX_DESCRIPTOR_SETS> update_data{};
std::array<bool, MAX_DESCRIPTOR_SETS> descriptor_dirty{};
std::array<vk::DescriptorSet, MAX_DESCRIPTOR_SETS> descriptor_sets;
bool state_dirty = true;
// Bound shader modules
enum ProgramType : u32 { VS = 0, GS = 2, FS = 1 };

View File

@ -532,7 +532,7 @@ bool RasterizerVulkan::AccelerateDrawBatchInternal(bool is_indexed) {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
if (is_indexed) {
bool index_u16 = regs.pipeline.index_array.format != 0;
const u64 index_buffer_size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1);
const u32 index_buffer_size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1);
if (index_buffer_size > INDEX_BUFFER_SIZE) {
LOG_WARNING(Render_Vulkan, "Too large index input size {}", index_buffer_size);
@ -681,10 +681,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
const auto BindSampler = [&](u32 binding, SamplerInfo& info,
const Pica::TexturingRegs::TextureConfig& config) {
// TODO(GPUCode): Cubemaps don't contain any mipmaps for now, so sampling from them returns
// nothing Always sample from the base level until mipmaps for texture cubes are implemented
// NOTE: There are no Vulkan filter modes that directly correspond to OpenGL minification
// filters GL_LINEAR/GL_NEAREST so emulate them by setting minLod = 0, and maxLod = 0.25,
// and using minFilter = VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST
// nothing. Always sample from the base level until mipmaps for texture cubes are
// implemented
const bool skip_mipmap = config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
info = SamplerInfo{.mag_filter = config.mag_filter,
.min_filter = config.min_filter,
@ -783,7 +781,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// the geometry in question.
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
// on the male character's face, which in the OpenGL default appear black.
// state.texture_units[texture_index].texture_2d = default_texture;
pipeline_cache.BindTexture(texture_index, default_texture.image_view);
}
} else {
@ -1725,12 +1722,6 @@ 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);
}
@ -1846,10 +1837,6 @@ 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() {
@ -1857,12 +1844,6 @@ void RasterizerVulkan::SyncDepthWriteMask() {
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);
}
@ -1873,21 +1854,6 @@ void RasterizerVulkan::SyncStencilTest() {
const bool test_enable = stencil_test.enable && regs.framebuffer.framebuffer.depth_format ==
Pica::FramebufferRegs::DepthFormat::D24S8;
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);
@ -1906,12 +1872,6 @@ void RasterizerVulkan::SyncDepthTest() {
? regs.framebuffer.output_merger.depth_test_func.Value()
: 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);
}
@ -1991,7 +1951,7 @@ void RasterizerVulkan::SyncLightPosition(int light_index) {
void RasterizerVulkan::SyncLightSpotDirection(int light_index) {
const auto& light = Pica::g_state.regs.lighting.light[light_index];
const auto spot_direction = Common::Vec3f{light.spot_x, light.spot_y, light.spot_z} / 2047.0f;
const auto spot_direction = Common::Vec3i{light.spot_x, light.spot_y, light.spot_z} / 2047.0f;
if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) {
uniform_block_data.data.light_src[light_index].spot_direction = spot_direction;