Files
parallel-gx1/Source/Core/VideoBackends/Vulkan/StateTracker.h
T
LibretroAdmin 441a0f1762 Vulkan: cull mode and depth state per draw, one pipeline for the rest
Where the device has VK_EXT_extended_dynamic_state, the cull mode and
the depth test, write and compare function of a GX or ubershader
pipeline are set per draw, and pipelines that differ only there are one
VkPipeline: a pipeline asked for whose shaders, vertex layout, blending,
topology and framebuffer match one already built takes that one at once
instead of being compiled. Fewer pipelines are compiled, in the
background and on the spot alike.

The shared pipelines sit in a fixed open-addressed table every thread
building pipelines reads and adds to without a lock, counted by the
pipelines using them; a shader is told apart by a number given it when
it is made, not by its address. The state tracker sets only what
differs from what the command buffer has set already, and sets it again
after a pipeline with the state built in.

The colour blend attachments of a pipeline are kept on the stack.
2026-11-07 00:42:47 +01:00

174 lines
6.0 KiB
C++

// Copyright 2016 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
#include "VideoCommon/Constants.h"
namespace Vulkan
{
class VKFramebuffer;
class VKShader;
class VKPipeline;
class VKTexture;
class StreamBuffer;
class VertexFormat;
class StateTracker
{
public:
StateTracker();
~StateTracker();
static StateTracker* GetInstance();
static bool CreateInstance();
static void DestroyInstance();
VKFramebuffer* GetFramebuffer() const { return m_framebuffer; }
const VKPipeline* GetPipeline() const { return m_pipeline; }
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset, u32 size);
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
void SetFramebuffer(VKFramebuffer* framebuffer);
void SetPipeline(const VKPipeline* pipeline);
void SetComputeShader(const VKShader* shader);
void SetGXUniformBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
void SetUtilityUniformBuffer(VkBuffer buffer, u32 offset, u32 size);
void SetTexture(u32 index, VkImageView view);
void SetSampler(u32 index, VkSampler sampler);
void SetSSBO(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range);
void SetTexelBuffer(u32 index, VkBufferView view);
void SetImageTexture(u32 index, VkImageView view);
void UnbindTexture(VkImageView view);
// Set dirty flags on everything to force re-bind at next draw time.
void InvalidateCachedState();
// Ends a render pass if we're currently in one.
// When Bind() is next called, the pass will be restarted.
// Calling this function is allowed even if a pass has not begun.
bool InRenderPass() const { return m_current_render_pass != VK_NULL_HANDLE; }
void BeginRenderPass();
void BeginDiscardRenderPass();
void EndRenderPass();
// Ends the current render pass if it was a clear render pass.
void BeginClearRenderPass(const VkRect2D& area, const VkClearValue* clear_values,
u32 num_clear_values);
void EndClearRenderPass();
void SetViewport(const VkViewport& viewport);
void SetScissor(const VkRect2D& scissor);
// Binds all dirty state to the command buffer.
// If this returns false, you should not issue the draw.
bool Bind();
// Binds all dirty compute state to the command buffer.
// If this returns false, you should not dispatch the shader.
bool BindCompute();
// Returns true if the specified rectangle is inside the current render area (used for clears).
bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const;
private:
void BindDynamicState(VkCommandBuffer command_buffer, u32 state);
// Number of descriptor sets for game draws.
enum
{
NUM_GX_DESCRIPTOR_SETS = 3,
NUM_UTILITY_DESCRIPTOR_SETS = 2,
NUM_COMPUTE_DESCRIPTOR_SETS = 1
};
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_GX_UBOS = (1 << 0),
DIRTY_FLAG_GX_UBO_OFFSETS = (1 << 1),
DIRTY_FLAG_GX_SAMPLERS = (1 << 4),
DIRTY_FLAG_GX_SSBO = (1 << 5),
DIRTY_FLAG_UTILITY_UBO = (1 << 2),
DIRTY_FLAG_UTILITY_UBO_OFFSET = (1 << 3),
DIRTY_FLAG_UTILITY_BINDINGS = (1 << 6),
DIRTY_FLAG_COMPUTE_BINDINGS = (1 << 7),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 8),
DIRTY_FLAG_INDEX_BUFFER = (1 << 9),
DIRTY_FLAG_VIEWPORT = (1 << 10),
DIRTY_FLAG_SCISSOR = (1 << 11),
DIRTY_FLAG_PIPELINE = (1 << 12),
DIRTY_FLAG_COMPUTE_SHADER = (1 << 13),
DIRTY_FLAG_DESCRIPTOR_SETS = (1 << 14),
DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET = (1 << 15),
DIRTY_FLAG_ALL_DESCRIPTORS = DIRTY_FLAG_GX_UBOS | DIRTY_FLAG_UTILITY_UBO |
DIRTY_FLAG_GX_SAMPLERS | DIRTY_FLAG_GX_SSBO |
DIRTY_FLAG_UTILITY_BINDINGS | DIRTY_FLAG_COMPUTE_BINDINGS
};
bool Initialize();
// Check that the specified viewport is within the render area.
// If not, ends the render pass if it is a clear render pass.
bool IsViewportWithinRenderArea() const;
void UpdateDescriptorSet();
void UpdateGXDescriptorSet();
void UpdateUtilityDescriptorSet();
void UpdateComputeDescriptorSet();
// Which bindings/state has to be updated before the next draw.
u32 m_dirty_flags = 0;
// input assembly
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
VkDeviceSize m_vertex_buffer_offset = 0;
VkBuffer m_index_buffer = VK_NULL_HANDLE;
VkDeviceSize m_index_buffer_offset = 0;
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
// cull mode and depth state set per draw (VK_DYNAMIC_*), and the command buffer they were set
// in (its fence counter): 0 when they are not set in the current one
u64 m_dynamic_fence = 0;
u32 m_dynamic_state = 0;
// pipeline state
const VKPipeline* m_pipeline = nullptr;
const VKShader* m_compute_shader = nullptr;
// shader bindings
struct
{
std::array<VkDescriptorBufferInfo, NUM_UBO_DESCRIPTOR_SET_BINDINGS> gx_ubo_bindings;
std::array<u32, NUM_UBO_DESCRIPTOR_SET_BINDINGS> gx_ubo_offsets;
VkDescriptorBufferInfo utility_ubo_binding;
u32 utility_ubo_offset;
std::array<VkDescriptorImageInfo, VideoCommon::MAX_PIXEL_SHADER_SAMPLERS> samplers;
std::array<VkBufferView, NUM_COMPUTE_TEXEL_BUFFERS> texel_buffers;
VkDescriptorBufferInfo ssbo;
VkDescriptorBufferInfo gx_uber_vertex_ssbo;
std::array<VkDescriptorImageInfo, VideoCommon::MAX_COMPUTE_SHADER_SAMPLERS> image_textures;
} m_bindings = {};
std::array<VkDescriptorSet, NUM_GX_DESCRIPTOR_SETS> m_gx_descriptor_sets = {};
std::array<VkDescriptorSet, NUM_UTILITY_DESCRIPTOR_SETS> m_utility_descriptor_sets = {};
VkDescriptorSet m_compute_descriptor_set = VK_NULL_HANDLE;
// rasterization
VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
VkRect2D m_scissor = {{0, 0}, {1, 1}};
// uniform buffers
std::unique_ptr<VKTexture> m_dummy_texture;
std::unique_ptr<VKTexture> m_dummy_compute_texture;
VKFramebuffer* m_framebuffer = nullptr;
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
VkRect2D m_framebuffer_render_area = {};
};
} // namespace Vulkan