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