mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Move shader caches to VideoCommon
This commit is contained in:
@ -48,8 +48,7 @@ enum DESCRIPTOR_SET_BIND_POINT
|
||||
// - Standard
|
||||
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
|
||||
// - 8 combined image samplers (accessible from PS)
|
||||
// - BBox Enabled
|
||||
// - Same as standard, plus a single SSBO accessible from PS
|
||||
// - 1 SSBO accessible from PS if supported
|
||||
// - Push Constant
|
||||
// - Same as standard, plus 128 bytes of push constants, accessible from all stages.
|
||||
// - Texture Decoding
|
||||
@ -67,7 +66,6 @@ enum DESCRIPTOR_SET_BIND_POINT
|
||||
enum PIPELINE_LAYOUT
|
||||
{
|
||||
PIPELINE_LAYOUT_STANDARD,
|
||||
PIPELINE_LAYOUT_BBOX,
|
||||
PIPELINE_LAYOUT_PUSH_CONSTANT,
|
||||
PIPELINE_LAYOUT_TEXTURE_CONVERSION,
|
||||
PIPELINE_LAYOUT_UTILITY,
|
||||
|
@ -109,6 +109,9 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
||||
static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = {
|
||||
0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT};
|
||||
|
||||
// The geometry shader buffer must be last in this binding set, as we don't include it
|
||||
// if geometry shaders are not supported by the device. See the decrement below.
|
||||
static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = {
|
||||
{UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT},
|
||||
@ -139,7 +142,7 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
||||
{7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT},
|
||||
};
|
||||
|
||||
static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
|
||||
VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
|
||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings},
|
||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
@ -153,6 +156,10 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(compute_set_bindings)), compute_set_bindings}};
|
||||
|
||||
// Don't set the GS bit if geometry shaders aren't available.
|
||||
if (!g_vulkan_context->SupportsGeometryShaders())
|
||||
create_infos[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS].bindingCount--;
|
||||
|
||||
for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++)
|
||||
{
|
||||
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i],
|
||||
@ -180,11 +187,10 @@ bool ObjectCache::CreatePipelineLayouts()
|
||||
{
|
||||
VkResult res;
|
||||
|
||||
// Descriptor sets for each pipeline layout
|
||||
// Descriptor sets for each pipeline layout.
|
||||
// In the standard set, the SSBO must be the last descriptor, as we do not include it
|
||||
// when fragment stores and atomics are not supported by the device.
|
||||
VkDescriptorSetLayout standard_sets[] = {
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
|
||||
VkDescriptorSetLayout bbox_sets[] = {
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]};
|
||||
@ -207,10 +213,6 @@ bool ObjectCache::CreatePipelineLayouts()
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
|
||||
|
||||
// BBox
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(bbox_sets)), bbox_sets, 0, nullptr},
|
||||
|
||||
// Push Constant
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range},
|
||||
@ -228,6 +230,10 @@ bool ObjectCache::CreatePipelineLayouts()
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}};
|
||||
|
||||
// If bounding box is unsupported, don't bother with the SSBO descriptor set.
|
||||
if (!g_vulkan_context->SupportsBoundingBox())
|
||||
pipeline_layout_info[PIPELINE_LAYOUT_STANDARD].setLayoutCount--;
|
||||
|
||||
for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++)
|
||||
{
|
||||
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i],
|
||||
|
@ -224,7 +224,7 @@ std::tuple<VkBuffer, u32> Renderer::UpdateUtilityUniformBuffer(const void* unifo
|
||||
|
||||
void Renderer::SetPipeline(const AbstractPipeline* pipeline)
|
||||
{
|
||||
m_graphics_pipeline = static_cast<const VKPipeline*>(pipeline);
|
||||
StateTracker::GetInstance()->SetPipeline(static_cast<const VKPipeline*>(pipeline));
|
||||
}
|
||||
|
||||
void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
|
||||
@ -305,7 +305,7 @@ void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, cons
|
||||
// Build commands.
|
||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_graphics_pipeline->GetPipeline());
|
||||
StateTracker::GetInstance()->GetPipeline()->GetVkPipeline());
|
||||
if (vertex_buffer != VK_NULL_HANDLE)
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset);
|
||||
|
||||
@ -759,9 +759,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
|
||||
// Pull in now-ready async shaders.
|
||||
g_shader_cache->RetrieveAsyncShaders();
|
||||
}
|
||||
|
||||
void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region)
|
||||
@ -975,10 +972,8 @@ void Renderer::CheckForConfigChanges()
|
||||
RecreateEFBFramebuffer();
|
||||
RecompileShaders();
|
||||
FramebufferManager::GetInstance()->RecompileShaders();
|
||||
g_shader_cache->ReloadShaderAndPipelineCaches();
|
||||
g_shader_cache->ReloadPipelineCache();
|
||||
g_shader_cache->RecompileSharedShaders();
|
||||
StateTracker::GetInstance()->InvalidateShaderPointers();
|
||||
StateTracker::GetInstance()->ReloadPipelineUIDCache();
|
||||
}
|
||||
|
||||
// For vsync, we need to change the present mode, which means recreating the swap chain.
|
||||
@ -1021,8 +1016,6 @@ void Renderer::BindEFBToStateTracker()
|
||||
FramebufferManager::GetInstance()->GetEFBClearRenderPass());
|
||||
StateTracker::GetInstance()->SetFramebuffer(
|
||||
FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size);
|
||||
StateTracker::GetInstance()->SetMultisamplingstate(
|
||||
FramebufferManager::GetInstance()->GetEFBMultisamplingState());
|
||||
m_current_framebuffer = nullptr;
|
||||
m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth();
|
||||
m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight();
|
||||
@ -1125,21 +1118,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
num_clear_values);
|
||||
}
|
||||
|
||||
void Renderer::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetRasterizationState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetDepthState(const DepthState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetDepthState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetBlendingState(const BlendingState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetBlendState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
|
||||
{
|
||||
// Texture should always be in SHADER_READ_ONLY layout prior to use.
|
||||
|
@ -77,10 +77,7 @@ public:
|
||||
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value = {},
|
||||
float depth_value = 0.0f) override;
|
||||
void SetBlendingState(const BlendingState& state) override;
|
||||
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
|
||||
void SetRasterizationState(const RasterizationState& state) override;
|
||||
void SetDepthState(const DepthState& state) override;
|
||||
void SetTexture(u32 index, const AbstractTexture* texture) override;
|
||||
void SetSamplerState(u32 index, const SamplerState& state) override;
|
||||
void UnbindTexture(const AbstractTexture* texture) override;
|
||||
@ -135,6 +132,5 @@ private:
|
||||
|
||||
// Shaders used for clear/blit.
|
||||
VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE;
|
||||
const VKPipeline* m_graphics_pipeline = nullptr;
|
||||
};
|
||||
}
|
||||
|
@ -23,12 +23,7 @@
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
@ -41,7 +36,6 @@ ShaderCache::ShaderCache()
|
||||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
DestroyPipelineCache();
|
||||
DestroyShaderCaches();
|
||||
DestroySharedShaders();
|
||||
}
|
||||
|
||||
@ -49,7 +43,6 @@ bool ShaderCache::Initialize()
|
||||
{
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
LoadShaderCaches();
|
||||
if (!LoadPipelineCache())
|
||||
return false;
|
||||
}
|
||||
@ -62,21 +55,11 @@ bool ShaderCache::Initialize()
|
||||
if (!CompileSharedShaders())
|
||||
return false;
|
||||
|
||||
m_async_shader_compiler = std::make_unique<VideoCommon::AsyncShaderCompiler>();
|
||||
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ?
|
||||
g_ActiveConfig.GetShaderPrecompilerThreads() :
|
||||
g_ActiveConfig.GetShaderCompilerThreads());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::Shutdown()
|
||||
{
|
||||
if (m_async_shader_compiler)
|
||||
{
|
||||
m_async_shader_compiler->StopWorkerThreads();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShaderCache && m_pipeline_cache != VK_NULL_HANDLE)
|
||||
SavePipelineCache();
|
||||
}
|
||||
@ -392,40 +375,14 @@ VkPipeline ShaderCache::CreatePipeline(const PipelineInfo& info)
|
||||
}
|
||||
|
||||
VkPipeline ShaderCache::GetPipeline(const PipelineInfo& info)
|
||||
{
|
||||
return GetPipelineWithCacheResult(info).first;
|
||||
}
|
||||
|
||||
std::pair<VkPipeline, bool> ShaderCache::GetPipelineWithCacheResult(const PipelineInfo& info)
|
||||
{
|
||||
auto iter = m_pipeline_objects.find(info);
|
||||
if (iter != m_pipeline_objects.end())
|
||||
{
|
||||
// If it's background compiling, ignore it, and recompile it synchronously.
|
||||
if (!iter->second.second)
|
||||
return std::make_pair(iter->second.first, true);
|
||||
else
|
||||
m_pipeline_objects.erase(iter);
|
||||
}
|
||||
return iter->second;
|
||||
|
||||
VkPipeline pipeline = CreatePipeline(info);
|
||||
m_pipeline_objects.emplace(info, std::make_pair(pipeline, false));
|
||||
_assert_(pipeline != VK_NULL_HANDLE);
|
||||
return {pipeline, false};
|
||||
}
|
||||
|
||||
std::pair<std::pair<VkPipeline, bool>, bool>
|
||||
ShaderCache::GetPipelineWithCacheResultAsync(const PipelineInfo& info)
|
||||
{
|
||||
auto iter = m_pipeline_objects.find(info);
|
||||
if (iter != m_pipeline_objects.end())
|
||||
return std::make_pair(iter->second, true);
|
||||
|
||||
// Kick a job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<PipelineCompilerWorkItem>(info));
|
||||
m_pipeline_objects.emplace(info, std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair(std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true), false);
|
||||
m_pipeline_objects.emplace(info, pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VkPipeline ShaderCache::CreateComputePipeline(const ComputePipelineInfo& info)
|
||||
@ -465,11 +422,10 @@ VkPipeline ShaderCache::GetComputePipeline(const ComputePipelineInfo& info)
|
||||
|
||||
void ShaderCache::ClearPipelineCache()
|
||||
{
|
||||
// TODO: Stop any async compiling happening.
|
||||
for (const auto& it : m_pipeline_objects)
|
||||
{
|
||||
if (it.second.first != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second.first, nullptr);
|
||||
if (it.second != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr);
|
||||
}
|
||||
m_pipeline_objects.clear();
|
||||
|
||||
@ -673,266 +629,6 @@ void ShaderCache::SavePipelineCache()
|
||||
disk_cache.Close();
|
||||
}
|
||||
|
||||
// Cache inserter that is called back when reading from the file
|
||||
template <typename Uid>
|
||||
struct ShaderCacheReader : public LinearDiskCacheReader<Uid, u32>
|
||||
{
|
||||
ShaderCacheReader(std::map<Uid, std::pair<VkShaderModule, bool>>& shader_map)
|
||||
: m_shader_map(shader_map)
|
||||
{
|
||||
}
|
||||
void Read(const Uid& key, const u32* value, u32 value_size) override
|
||||
{
|
||||
// We don't insert null modules into the shader map since creation could succeed later on.
|
||||
// e.g. we're generating bad code, but fix this in a later version, and for some reason
|
||||
// the cache is not invalidated.
|
||||
VkShaderModule module = Util::CreateShaderModule(value, value_size);
|
||||
if (module == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
m_shader_map.emplace(key, std::make_pair(module, false));
|
||||
}
|
||||
|
||||
std::map<Uid, std::pair<VkShaderModule, bool>>& m_shader_map;
|
||||
};
|
||||
|
||||
void ShaderCache::LoadShaderCaches()
|
||||
{
|
||||
ShaderCacheReader<VertexShaderUid> vs_reader(m_vs_cache.shader_map);
|
||||
m_vs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "VS", true, true),
|
||||
vs_reader);
|
||||
|
||||
ShaderCacheReader<PixelShaderUid> ps_reader(m_ps_cache.shader_map);
|
||||
m_ps_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "PS", true, true),
|
||||
ps_reader);
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
ShaderCacheReader<GeometryShaderUid> gs_reader(m_gs_cache.shader_map);
|
||||
m_gs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "GS", true, true),
|
||||
gs_reader);
|
||||
}
|
||||
|
||||
ShaderCacheReader<UberShader::VertexShaderUid> uber_vs_reader(m_uber_vs_cache.shader_map);
|
||||
m_uber_vs_cache.disk_cache.OpenAndRead(
|
||||
GetDiskShaderCacheFileName(APIType::Vulkan, "UberVS", false, true), uber_vs_reader);
|
||||
ShaderCacheReader<UberShader::PixelShaderUid> uber_ps_reader(m_uber_ps_cache.shader_map);
|
||||
m_uber_ps_cache.disk_cache.OpenAndRead(
|
||||
GetDiskShaderCacheFileName(APIType::Vulkan, "UberPS", false, true), uber_ps_reader);
|
||||
|
||||
SETSTAT(stats.numPixelShadersCreated, static_cast<int>(m_ps_cache.shader_map.size()));
|
||||
SETSTAT(stats.numPixelShadersAlive, static_cast<int>(m_ps_cache.shader_map.size()));
|
||||
SETSTAT(stats.numVertexShadersCreated, static_cast<int>(m_vs_cache.shader_map.size()));
|
||||
SETSTAT(stats.numVertexShadersAlive, static_cast<int>(m_vs_cache.shader_map.size()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void DestroyShaderCache(T& cache)
|
||||
{
|
||||
cache.disk_cache.Sync();
|
||||
cache.disk_cache.Close();
|
||||
for (const auto& it : cache.shader_map)
|
||||
{
|
||||
if (it.second.first != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second.first, nullptr);
|
||||
}
|
||||
cache.shader_map.clear();
|
||||
}
|
||||
|
||||
void ShaderCache::DestroyShaderCaches()
|
||||
{
|
||||
DestroyShaderCache(m_vs_cache);
|
||||
DestroyShaderCache(m_ps_cache);
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
DestroyShaderCache(m_gs_cache);
|
||||
|
||||
DestroyShaderCache(m_uber_vs_cache);
|
||||
DestroyShaderCache(m_uber_ps_cache);
|
||||
|
||||
SETSTAT(stats.numPixelShadersCreated, 0);
|
||||
SETSTAT(stats.numPixelShadersAlive, 0);
|
||||
SETSTAT(stats.numVertexShadersCreated, 0);
|
||||
SETSTAT(stats.numVertexShadersAlive, 0);
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetVertexShaderForUid(const VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_vs_cache.shader_map.find(uid);
|
||||
if (it != m_vs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_vs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numVertexShadersCreated);
|
||||
INCSTAT(stats.numVertexShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetGeometryShaderForUid(const GeometryShaderUid& uid)
|
||||
{
|
||||
_assert_(g_vulkan_context->SupportsGeometryShaders());
|
||||
auto it = m_gs_cache.shader_map.find(uid);
|
||||
if (it != m_gs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_gs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GenerateGeometryShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileGeometryShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
m_gs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_gs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetPixelShaderForUid(const PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_ps_cache.shader_map.find(uid);
|
||||
if (it != m_ps_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_ps_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numPixelShadersCreated);
|
||||
INCSTAT(stats.numPixelShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_uber_vs_cache.shader_map.find(uid);
|
||||
if (it != m_uber_vs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_uber_vs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code = UberShader::GenVertexShader(
|
||||
APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_uber_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numVertexShadersCreated);
|
||||
INCSTAT(stats.numVertexShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_uber_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_uber_ps_cache.shader_map.find(uid);
|
||||
if (it != m_uber_ps_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_uber_ps_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
UberShader::GenPixelShader(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_uber_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numPixelShadersCreated);
|
||||
INCSTAT(stats.numPixelShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_uber_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
void ShaderCache::RecompileSharedShaders()
|
||||
{
|
||||
DestroySharedShaders();
|
||||
@ -940,27 +636,15 @@ void ShaderCache::RecompileSharedShaders()
|
||||
PanicAlert("Failed to recompile shared shaders.");
|
||||
}
|
||||
|
||||
void ShaderCache::ReloadShaderAndPipelineCaches()
|
||||
void ShaderCache::ReloadPipelineCache()
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
|
||||
SavePipelineCache();
|
||||
DestroyShaderCaches();
|
||||
DestroyPipelineCache();
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
LoadShaderCaches();
|
||||
LoadPipelineCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatePipelineCache();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileUberShaders();
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetUtilityShaderHeader() const
|
||||
@ -1160,203 +844,4 @@ void ShaderCache::DestroySharedShaders()
|
||||
DestroyShader(m_screen_quad_geometry_shader);
|
||||
DestroyShader(m_passthrough_geometry_shader);
|
||||
}
|
||||
|
||||
void ShaderCache::CreateDummyPipeline(const UberShader::VertexShaderUid& vuid,
|
||||
const GeometryShaderUid& guid,
|
||||
const UberShader::PixelShaderUid& puid)
|
||||
{
|
||||
PortableVertexDeclaration vertex_decl;
|
||||
std::memset(&vertex_decl, 0, sizeof(vertex_decl));
|
||||
|
||||
PipelineInfo pinfo;
|
||||
pinfo.vertex_format =
|
||||
static_cast<const VertexFormat*>(VertexLoaderManager::GetUberVertexFormat(vertex_decl));
|
||||
pinfo.pipeline_layout = g_object_cache->GetPipelineLayout(
|
||||
g_ActiveConfig.bBBoxEnable && g_ActiveConfig.BBoxUseFragmentShaderImplementation() ?
|
||||
PIPELINE_LAYOUT_BBOX :
|
||||
PIPELINE_LAYOUT_STANDARD);
|
||||
pinfo.vs = GetVertexUberShaderForUid(vuid);
|
||||
pinfo.gs = (!guid.GetUidData()->IsPassthrough() && g_vulkan_context->SupportsGeometryShaders()) ?
|
||||
GetGeometryShaderForUid(guid) :
|
||||
VK_NULL_HANDLE;
|
||||
pinfo.ps = GetPixelUberShaderForUid(puid);
|
||||
pinfo.render_pass = FramebufferManager::GetInstance()->GetEFBLoadRenderPass();
|
||||
pinfo.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex;
|
||||
pinfo.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex;
|
||||
pinfo.blend_state.hex = RenderState::GetNoBlendingBlendState().hex;
|
||||
pinfo.multisampling_state.hex = FramebufferManager::GetInstance()->GetEFBMultisamplingState().hex;
|
||||
pinfo.rasterization_state.primitive =
|
||||
static_cast<PrimitiveType>(guid.GetUidData()->primitive_type);
|
||||
GetPipelineWithCacheResultAsync(pinfo);
|
||||
}
|
||||
|
||||
void ShaderCache::PrecompileUberShaders()
|
||||
{
|
||||
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
|
||||
UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
|
||||
// UIDs must have compatible texgens, a mismatching combination will never be queried.
|
||||
if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
|
||||
if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
CreateDummyPipeline(vuid, guid, puid);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
WaitForBackgroundCompilesToComplete();
|
||||
|
||||
// Switch to the runtime/background thread config.
|
||||
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
|
||||
}
|
||||
|
||||
void ShaderCache::WaitForBackgroundCompilesToComplete()
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
|
||||
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
|
||||
static_cast<int>(completed), static_cast<int>(total));
|
||||
});
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
Host_UpdateProgressDialog("", -1, -1);
|
||||
}
|
||||
|
||||
void ShaderCache::RetrieveAsyncShaders()
|
||||
{
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
std::pair<VkShaderModule, bool> ShaderCache::GetVertexShaderForUidAsync(const VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_vs_cache.shader_map.find(uid);
|
||||
if (it != m_vs_cache.shader_map.end())
|
||||
return it->second;
|
||||
|
||||
// Kick a compile job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<VertexShaderCompilerWorkItem>(uid));
|
||||
m_vs_cache.shader_map.emplace(uid,
|
||||
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
|
||||
}
|
||||
|
||||
std::pair<VkShaderModule, bool> ShaderCache::GetPixelShaderForUidAsync(const PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_ps_cache.shader_map.find(uid);
|
||||
if (it != m_ps_cache.shader_map.end())
|
||||
return it->second;
|
||||
|
||||
// Kick a compile job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<PixelShaderCompilerWorkItem>(uid));
|
||||
m_ps_cache.shader_map.emplace(uid,
|
||||
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
|
||||
}
|
||||
|
||||
bool ShaderCache::VertexShaderCompilerWorkItem::Compile()
|
||||
{
|
||||
ShaderCode code =
|
||||
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
|
||||
if (!ShaderCompiler::CompileVertexShader(&m_spirv, code.GetBuffer().c_str(),
|
||||
code.GetBuffer().length()))
|
||||
return true;
|
||||
|
||||
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::VertexShaderCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_vs_cache.shader_map.find(m_uid);
|
||||
if (it == g_shader_cache->m_vs_cache.shader_map.end())
|
||||
{
|
||||
g_shader_cache->m_vs_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
|
||||
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_module != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_module;
|
||||
it->second.second = false;
|
||||
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
}
|
||||
|
||||
bool ShaderCache::PixelShaderCompilerWorkItem::Compile()
|
||||
{
|
||||
ShaderCode code =
|
||||
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
|
||||
if (!ShaderCompiler::CompileFragmentShader(&m_spirv, code.GetBuffer().c_str(),
|
||||
code.GetBuffer().length()))
|
||||
return true;
|
||||
|
||||
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::PixelShaderCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_ps_cache.shader_map.find(m_uid);
|
||||
if (it == g_shader_cache->m_ps_cache.shader_map.end())
|
||||
{
|
||||
g_shader_cache->m_ps_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
|
||||
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_module != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_module;
|
||||
it->second.second = false;
|
||||
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
}
|
||||
|
||||
bool ShaderCache::PipelineCompilerWorkItem::Compile()
|
||||
{
|
||||
m_pipeline = g_shader_cache->CreatePipeline(m_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::PipelineCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_pipeline_objects.find(m_info);
|
||||
if (it == g_shader_cache->m_pipeline_objects.end())
|
||||
{
|
||||
g_shader_cache->m_pipeline_objects.emplace(m_info, std::make_pair(m_pipeline, false));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_pipeline != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_pipeline;
|
||||
it->second.second = false;
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,7 @@
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
@ -33,10 +27,6 @@ class CommandBufferManager;
|
||||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
class CommandBufferManager;
|
||||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
struct PipelineInfo
|
||||
{
|
||||
// These are packed in descending order of size, to avoid any padding so that the structure
|
||||
@ -88,19 +78,6 @@ public:
|
||||
// Get utility shader header based on current config.
|
||||
std::string GetUtilityShaderHeader() const;
|
||||
|
||||
// Accesses ShaderGen shader caches
|
||||
VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid);
|
||||
VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid);
|
||||
VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid);
|
||||
|
||||
// Ubershader caches
|
||||
VkShaderModule GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid);
|
||||
VkShaderModule GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid);
|
||||
|
||||
// Accesses ShaderGen shader caches asynchronously
|
||||
std::pair<VkShaderModule, bool> GetVertexShaderForUidAsync(const VertexShaderUid& uid);
|
||||
std::pair<VkShaderModule, bool> GetPixelShaderForUidAsync(const PixelShaderUid& uid);
|
||||
|
||||
// Perform at startup, create descriptor layouts, compiles all static shaders.
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
@ -112,13 +89,6 @@ public:
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it.
|
||||
VkPipeline GetPipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it. If this
|
||||
// resulted in a pipeline being created, the second field of the return value will be false,
|
||||
// otherwise for a cache hit it will be true.
|
||||
std::pair<VkPipeline, bool> GetPipelineWithCacheResult(const PipelineInfo& info);
|
||||
std::pair<std::pair<VkPipeline, bool>, bool>
|
||||
GetPipelineWithCacheResultAsync(const PipelineInfo& info);
|
||||
|
||||
// Creates a compute pipeline, and does not track the handle.
|
||||
VkPipeline CreateComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
@ -139,47 +109,22 @@ public:
|
||||
void RecompileSharedShaders();
|
||||
|
||||
// Reload pipeline cache. This will destroy all pipelines.
|
||||
void ReloadShaderAndPipelineCaches();
|
||||
void ReloadPipelineCache();
|
||||
|
||||
// Shared shader accessors
|
||||
VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; }
|
||||
VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; }
|
||||
VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; }
|
||||
VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; }
|
||||
void PrecompileUberShaders();
|
||||
void WaitForBackgroundCompilesToComplete();
|
||||
void RetrieveAsyncShaders();
|
||||
|
||||
private:
|
||||
bool CreatePipelineCache();
|
||||
bool LoadPipelineCache();
|
||||
bool ValidatePipelineCache(const u8* data, size_t data_length);
|
||||
void DestroyPipelineCache();
|
||||
void LoadShaderCaches();
|
||||
void DestroyShaderCaches();
|
||||
bool CompileSharedShaders();
|
||||
void DestroySharedShaders();
|
||||
|
||||
// We generate a dummy pipeline with some defaults in the blend/depth states,
|
||||
// that way the driver is forced to compile something (looking at you, NVIDIA).
|
||||
// It can then hopefully re-use part of this pipeline for others in the future.
|
||||
void CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, const GeometryShaderUid& guid,
|
||||
const UberShader::PixelShaderUid& puid);
|
||||
|
||||
template <typename Uid>
|
||||
struct ShaderModuleCache
|
||||
{
|
||||
std::map<Uid, std::pair<VkShaderModule, bool>> shader_map;
|
||||
LinearDiskCache<Uid, u32> disk_cache;
|
||||
};
|
||||
ShaderModuleCache<VertexShaderUid> m_vs_cache;
|
||||
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
|
||||
ShaderModuleCache<PixelShaderUid> m_ps_cache;
|
||||
ShaderModuleCache<UberShader::VertexShaderUid> m_uber_vs_cache;
|
||||
ShaderModuleCache<UberShader::PixelShaderUid> m_uber_ps_cache;
|
||||
|
||||
std::unordered_map<PipelineInfo, std::pair<VkPipeline, bool>, PipelineInfoHash>
|
||||
m_pipeline_objects;
|
||||
std::unordered_map<PipelineInfo, VkPipeline, PipelineInfoHash> m_pipeline_objects;
|
||||
std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash>
|
||||
m_compute_pipeline_objects;
|
||||
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
|
||||
@ -190,45 +135,6 @@ private:
|
||||
VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE;
|
||||
|
||||
std::unique_ptr<VideoCommon::AsyncShaderCompiler> m_async_shader_compiler;
|
||||
|
||||
// TODO: Use templates to reduce the number of these classes.
|
||||
class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid) : m_uid(uid) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
VertexShaderUid m_uid;
|
||||
ShaderCompiler::SPIRVCodeVector m_spirv;
|
||||
VkShaderModule m_module = VK_NULL_HANDLE;
|
||||
};
|
||||
class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid) : m_uid(uid) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
PixelShaderUid m_uid;
|
||||
ShaderCompiler::SPIRVCodeVector m_spirv;
|
||||
VkShaderModule m_module = VK_NULL_HANDLE;
|
||||
};
|
||||
class PipelineCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit PipelineCompilerWorkItem(const PipelineInfo& info) : m_info(info) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
PipelineInfo m_info;
|
||||
VkPipeline m_pipeline;
|
||||
};
|
||||
};
|
||||
|
||||
extern std::unique_ptr<ShaderCache> g_shader_cache;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VKPipeline.h"
|
||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
@ -53,12 +54,6 @@ void StateTracker::DestroyInstance()
|
||||
|
||||
bool StateTracker::Initialize()
|
||||
{
|
||||
// BBox is disabled by default.
|
||||
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
m_bbox_enabled = false;
|
||||
ClearShaders();
|
||||
|
||||
// Initialize all samplers to point by default
|
||||
for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++)
|
||||
{
|
||||
@ -97,122 +92,6 @@ bool StateTracker::Initialize()
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateShaderPointers()
|
||||
{
|
||||
// Clear UIDs, forcing a false match next time.
|
||||
m_vs_uid = {};
|
||||
m_gs_uid = {};
|
||||
m_ps_uid = {};
|
||||
|
||||
// Invalidate shader pointers.
|
||||
m_pipeline_state.vs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.ps = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void StateTracker::ReloadPipelineUIDCache()
|
||||
{
|
||||
class PipelineInserter final : public LinearDiskCacheReader<SerializedPipelineUID, u32>
|
||||
{
|
||||
public:
|
||||
explicit PipelineInserter(StateTracker* this_ptr_) : this_ptr(this_ptr_) {}
|
||||
void Read(const SerializedPipelineUID& key, const u32* value, u32 value_size)
|
||||
{
|
||||
this_ptr->PrecachePipelineUID(key);
|
||||
}
|
||||
|
||||
private:
|
||||
StateTracker* this_ptr;
|
||||
};
|
||||
|
||||
m_uid_cache.Sync();
|
||||
m_uid_cache.Close();
|
||||
|
||||
// UID caches don't contain any host state, so use a single uid cache per gameid.
|
||||
std::string filename = GetDiskShaderCacheFileName(APIType::Vulkan, "PipelineUID", true, false);
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
PipelineInserter inserter(this);
|
||||
m_uid_cache.OpenAndRead(filename, inserter);
|
||||
}
|
||||
|
||||
// If we were using background compilation, ensure everything is ready before continuing.
|
||||
if (g_ActiveConfig.bBackgroundShaderCompiling)
|
||||
g_shader_cache->WaitForBackgroundCompilesToComplete();
|
||||
}
|
||||
|
||||
void StateTracker::AppendToPipelineUIDCache(const PipelineInfo& info)
|
||||
{
|
||||
SerializedPipelineUID sinfo;
|
||||
sinfo.blend_state_bits = info.blend_state.hex;
|
||||
sinfo.rasterizer_state_bits = info.rasterization_state.hex;
|
||||
sinfo.depth_state_bits = info.depth_state.hex;
|
||||
sinfo.vertex_decl = m_pipeline_state.vertex_format->GetVertexDeclaration();
|
||||
sinfo.vs_uid = m_vs_uid;
|
||||
sinfo.gs_uid = m_gs_uid;
|
||||
sinfo.ps_uid = m_ps_uid;
|
||||
|
||||
u32 dummy_value = 0;
|
||||
m_uid_cache.Append(sinfo, &dummy_value, 1);
|
||||
}
|
||||
|
||||
bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
||||
{
|
||||
PipelineInfo pinfo = {};
|
||||
|
||||
// Need to create the vertex declaration first, rather than deferring to when a game creates a
|
||||
// vertex loader that uses this format, since we need it to create a pipeline.
|
||||
pinfo.vertex_format =
|
||||
static_cast<VertexFormat*>(VertexLoaderManager::GetOrCreateMatchingFormat(uid.vertex_decl));
|
||||
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
pinfo.vs = g_shader_cache->GetVertexShaderForUid(uid.vs_uid);
|
||||
if (pinfo.vs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get vertex shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
if (g_vulkan_context->SupportsGeometryShaders() && !uid.gs_uid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
pinfo.gs = g_shader_cache->GetGeometryShaderForUid(uid.gs_uid);
|
||||
if (pinfo.gs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get geometry shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pinfo.ps = g_shader_cache->GetPixelShaderForUid(uid.ps_uid);
|
||||
if (pinfo.ps == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pixel shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
pinfo.render_pass = m_load_render_pass;
|
||||
pinfo.rasterization_state.hex = uid.rasterizer_state_bits;
|
||||
pinfo.depth_state.hex = uid.depth_state_bits;
|
||||
pinfo.blend_state.hex = uid.blend_state_bits;
|
||||
pinfo.multisampling_state.hex = m_pipeline_state.multisampling_state.hex;
|
||||
|
||||
if (g_ActiveConfig.bBackgroundShaderCompiling)
|
||||
{
|
||||
// Use async for multithreaded compilation.
|
||||
g_shader_cache->GetPipelineWithCacheResultAsync(pinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
VkPipeline pipeline = g_shader_cache->GetPipeline(pinfo);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pipeline from cached UID.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to do anything with this pipeline, just make sure it exists.
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset)
|
||||
{
|
||||
if (m_vertex_buffer == buffer && m_vertex_buffer_offset == offset)
|
||||
@ -238,14 +117,6 @@ void StateTracker::SetRenderPass(VkRenderPass load_render_pass, VkRenderPass cle
|
||||
{
|
||||
// Should not be changed within a render pass.
|
||||
_assert_(!InRenderPass());
|
||||
|
||||
// The clear and load render passes are compatible, so we don't need to change our pipeline.
|
||||
if (m_pipeline_state.render_pass != load_render_pass)
|
||||
{
|
||||
m_pipeline_state.render_pass = load_render_pass;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
m_load_render_pass = load_render_pass;
|
||||
m_clear_render_pass = clear_render_pass;
|
||||
}
|
||||
@ -258,169 +129,18 @@ void StateTracker::SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& ren
|
||||
m_framebuffer_size = render_area;
|
||||
}
|
||||
|
||||
void StateTracker::SetVertexFormat(const VertexFormat* vertex_format)
|
||||
void StateTracker::SetPipeline(const VKPipeline* pipeline)
|
||||
{
|
||||
if (m_vertex_format == vertex_format)
|
||||
if (m_pipeline == pipeline)
|
||||
return;
|
||||
|
||||
m_vertex_format = vertex_format;
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
void StateTracker::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
if (m_pipeline_state.rasterization_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.rasterization_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetMultisamplingstate(const MultisamplingState& state)
|
||||
{
|
||||
if (m_pipeline_state.multisampling_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.multisampling_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetDepthState(const DepthState& state)
|
||||
{
|
||||
if (m_pipeline_state.depth_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.depth_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetBlendState(const BlendingState& state)
|
||||
{
|
||||
if (m_pipeline_state.blend_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.blend_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
bool StateTracker::CheckForShaderChanges()
|
||||
{
|
||||
VertexShaderUid vs_uid = GetVertexShaderUid();
|
||||
PixelShaderUid ps_uid = GetPixelShaderUid();
|
||||
ClearUnusedPixelShaderUidBits(APIType::Vulkan, &ps_uid);
|
||||
|
||||
bool changed = false;
|
||||
bool use_ubershaders = g_ActiveConfig.bDisableSpecializedShaders;
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !g_ActiveConfig.bDisableSpecializedShaders)
|
||||
{
|
||||
// Look up both VS and PS, and check if we can compile it asynchronously.
|
||||
auto vs = g_shader_cache->GetVertexShaderForUidAsync(vs_uid);
|
||||
auto ps = g_shader_cache->GetPixelShaderForUidAsync(ps_uid);
|
||||
if (vs.second || ps.second)
|
||||
{
|
||||
// One of the shaders is still pending. Use the ubershader for both.
|
||||
use_ubershaders = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the standard shaders for both.
|
||||
if (m_pipeline_state.vs != vs.first)
|
||||
{
|
||||
m_pipeline_state.vs = vs.first;
|
||||
m_vs_uid = vs_uid;
|
||||
changed = true;
|
||||
}
|
||||
if (m_pipeline_state.ps != ps.first)
|
||||
{
|
||||
m_pipeline_state.ps = ps.first;
|
||||
m_ps_uid = ps_uid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal shader path. No ubershaders.
|
||||
if (vs_uid != m_vs_uid)
|
||||
{
|
||||
m_vs_uid = vs_uid;
|
||||
m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid);
|
||||
changed = true;
|
||||
}
|
||||
if (ps_uid != m_ps_uid)
|
||||
{
|
||||
m_ps_uid = ps_uid;
|
||||
m_pipeline_state.ps = g_shader_cache->GetPixelShaderForUid(ps_uid);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Switching to/from ubershaders? Have to adjust the vertex format and pipeline layout.
|
||||
if (use_ubershaders != m_using_ubershaders)
|
||||
{
|
||||
m_using_ubershaders = use_ubershaders;
|
||||
UpdatePipelineLayout();
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
if (use_ubershaders)
|
||||
{
|
||||
UberShader::VertexShaderUid uber_vs_uid = UberShader::GetVertexShaderUid();
|
||||
VkShaderModule vs = g_shader_cache->GetVertexUberShaderForUid(uber_vs_uid);
|
||||
if (vs != m_pipeline_state.vs)
|
||||
{
|
||||
m_uber_vs_uid = uber_vs_uid;
|
||||
m_pipeline_state.vs = vs;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
UberShader::PixelShaderUid uber_ps_uid = UberShader::GetPixelShaderUid();
|
||||
UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_ps_uid);
|
||||
VkShaderModule ps = g_shader_cache->GetPixelUberShaderForUid(uber_ps_uid);
|
||||
if (ps != m_pipeline_state.ps)
|
||||
{
|
||||
m_uber_ps_uid = uber_ps_uid;
|
||||
m_pipeline_state.ps = ps;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
GeometryShaderUid gs_uid = GetGeometryShaderUid(m_pipeline_state.rasterization_state.primitive);
|
||||
if (gs_uid != m_gs_uid)
|
||||
{
|
||||
m_gs_uid = gs_uid;
|
||||
if (gs_uid.GetUidData()->IsPassthrough())
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
else
|
||||
m_pipeline_state.gs = g_shader_cache->GetGeometryShaderForUid(gs_uid);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void StateTracker::ClearShaders()
|
||||
{
|
||||
// Set the UIDs to something that will never match, so on the first access they are checked.
|
||||
std::memset(&m_vs_uid, 0xFF, sizeof(m_vs_uid));
|
||||
std::memset(&m_gs_uid, 0xFF, sizeof(m_gs_uid));
|
||||
std::memset(&m_ps_uid, 0xFF, sizeof(m_ps_uid));
|
||||
std::memset(&m_uber_vs_uid, 0xFF, sizeof(m_uber_vs_uid));
|
||||
std::memset(&m_uber_ps_uid, 0xFF, sizeof(m_uber_ps_uid));
|
||||
|
||||
m_pipeline_state.vs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.ps = VK_NULL_HANDLE;
|
||||
m_pipeline_state.vertex_format = nullptr;
|
||||
const bool new_usage =
|
||||
pipeline && (!m_pipeline || m_pipeline->GetUsage() != pipeline->GetUsage());
|
||||
|
||||
m_pipeline = pipeline;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
if (new_usage)
|
||||
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
void StateTracker::UpdateVertexShaderConstants()
|
||||
@ -450,20 +170,6 @@ void StateTracker::UpdateVertexShaderConstants()
|
||||
|
||||
void StateTracker::UpdateGeometryShaderConstants()
|
||||
{
|
||||
// Skip updating geometry shader constants if it's not in use.
|
||||
if (m_pipeline_state.gs == VK_NULL_HANDLE)
|
||||
{
|
||||
// However, if the buffer has changed, we can't skip the update, because then we'll
|
||||
// try to include the now non-existant buffer in the descriptor set.
|
||||
if (m_uniform_stream_buffer->GetBuffer() ==
|
||||
m_bindings.uniform_buffer_bindings[UBO_DESCRIPTOR_SET_BINDING_GS].buffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GeometryShaderManager::dirty = true;
|
||||
}
|
||||
|
||||
if (!GeometryShaderManager::dirty || !ReserveConstantStorage())
|
||||
return;
|
||||
|
||||
@ -614,15 +320,6 @@ void StateTracker::SetSampler(size_t index, VkSampler sampler)
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SAMPLERS;
|
||||
}
|
||||
|
||||
void StateTracker::SetBBoxEnable(bool enable)
|
||||
{
|
||||
if (m_bbox_enabled == enable)
|
||||
return;
|
||||
|
||||
m_bbox_enabled = enable;
|
||||
UpdatePipelineLayout();
|
||||
}
|
||||
|
||||
void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range)
|
||||
{
|
||||
if (m_bindings.ps_ssbo.buffer == buffer && m_bindings.ps_ssbo.offset == offset &&
|
||||
@ -634,10 +331,7 @@ void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceS
|
||||
m_bindings.ps_ssbo.buffer = buffer;
|
||||
m_bindings.ps_ssbo.offset = offset;
|
||||
m_bindings.ps_ssbo.range = range;
|
||||
|
||||
// Defer descriptor update until bbox is actually enabled.
|
||||
if (IsSSBODescriptorRequired())
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
void StateTracker::UnbindTexture(VkImageView view)
|
||||
@ -653,10 +347,6 @@ void StateTracker::InvalidateDescriptorSets()
|
||||
{
|
||||
m_descriptor_sets.fill(VK_NULL_HANDLE);
|
||||
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
|
||||
|
||||
// Defer SSBO descriptor update until bbox is actually enabled.
|
||||
if (!IsSSBODescriptorRequired())
|
||||
m_dirty_flags &= ~DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateConstants()
|
||||
@ -669,9 +359,8 @@ void StateTracker::InvalidateConstants()
|
||||
void StateTracker::SetPendingRebind()
|
||||
{
|
||||
m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS | DIRTY_FLAG_DESCRIPTOR_SET_BINDING |
|
||||
DIRTY_FLAG_PIPELINE_BINDING | DIRTY_FLAG_VERTEX_BUFFER |
|
||||
DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
|
||||
DIRTY_FLAG_PIPELINE;
|
||||
DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT |
|
||||
DIRTY_FLAG_SCISSOR | DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::BeginRenderPass()
|
||||
@ -743,17 +432,14 @@ void StateTracker::SetScissor(const VkRect2D& scissor)
|
||||
|
||||
bool StateTracker::Bind(bool rebind_all /*= false*/)
|
||||
{
|
||||
// Must have a pipeline.
|
||||
if (!m_pipeline)
|
||||
return false;
|
||||
|
||||
// Check the render area if we were in a clear pass.
|
||||
if (m_current_render_pass == m_clear_render_pass && !IsViewportWithinRenderArea())
|
||||
EndRenderPass();
|
||||
|
||||
// Get new pipeline object if any parts have changed
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE && !UpdatePipeline())
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to get pipeline object, skipping draw");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a new descriptor set if any parts have changed
|
||||
if (m_dirty_flags & DIRTY_FLAG_ALL_DESCRIPTOR_SETS && !UpdateDescriptorSet())
|
||||
{
|
||||
@ -780,20 +466,20 @@ bool StateTracker::Bind(bool rebind_all /*= false*/)
|
||||
if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER || rebind_all)
|
||||
vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE_BINDING || rebind_all)
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_object);
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE || rebind_all)
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline());
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SET_BINDING || rebind_all)
|
||||
{
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_pipeline_state.pipeline_layout, 0, m_num_active_descriptor_sets,
|
||||
m_pipeline->GetVkPipelineLayout(), 0, m_num_active_descriptor_sets,
|
||||
m_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS,
|
||||
m_bindings.uniform_buffer_offsets.data());
|
||||
}
|
||||
else if (m_dirty_flags & DIRTY_FLAG_DYNAMIC_OFFSETS)
|
||||
{
|
||||
vkCmdBindDescriptorSets(
|
||||
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout,
|
||||
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(),
|
||||
DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1,
|
||||
&m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS],
|
||||
NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data());
|
||||
@ -933,105 +619,6 @@ void StateTracker::EndClearRenderPass()
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
VkPipeline StateTracker::GetPipelineAndCacheUID()
|
||||
{
|
||||
// We can't cache ubershader uids, only normal shader uids.
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !m_using_ubershaders)
|
||||
{
|
||||
// Append to UID cache if it is a new pipeline.
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResultAsync(m_pipeline_state);
|
||||
if (!result.second && g_ActiveConfig.bShaderCache)
|
||||
AppendToPipelineUIDCache(m_pipeline_state);
|
||||
|
||||
// Still waiting for the pipeline to compile?
|
||||
if (!result.first.second)
|
||||
return result.first.first;
|
||||
|
||||
// Use ubershader instead.
|
||||
m_using_ubershaders = true;
|
||||
UpdatePipelineLayout();
|
||||
UpdatePipelineVertexFormat();
|
||||
|
||||
PipelineInfo uber_info = m_pipeline_state;
|
||||
UberShader::VertexShaderUid uber_vuid = UberShader::GetVertexShaderUid();
|
||||
UberShader::PixelShaderUid uber_puid = UberShader::GetPixelShaderUid();
|
||||
UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_puid);
|
||||
uber_info.vs = g_shader_cache->GetVertexUberShaderForUid(uber_vuid);
|
||||
uber_info.ps = g_shader_cache->GetPixelUberShaderForUid(uber_puid);
|
||||
|
||||
auto uber_result = g_shader_cache->GetPipelineWithCacheResult(uber_info);
|
||||
return uber_result.first;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to the UID cache if it is a new pipeline.
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResult(m_pipeline_state);
|
||||
if (!result.second && !m_using_ubershaders && g_ActiveConfig.bShaderCache)
|
||||
AppendToPipelineUIDCache(m_pipeline_state);
|
||||
|
||||
return result.first;
|
||||
}
|
||||
}
|
||||
|
||||
bool StateTracker::IsSSBODescriptorRequired() const
|
||||
{
|
||||
return m_bbox_enabled || (m_using_ubershaders && g_ActiveConfig.bBBoxEnable &&
|
||||
g_ActiveConfig.BBoxUseFragmentShaderImplementation());
|
||||
}
|
||||
|
||||
bool StateTracker::UpdatePipeline()
|
||||
{
|
||||
// We need at least a vertex and fragment shader
|
||||
if (m_pipeline_state.vs == VK_NULL_HANDLE || m_pipeline_state.ps == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
// Grab a new pipeline object, this can fail.
|
||||
m_pipeline_object = GetPipelineAndCacheUID();
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE_BINDING;
|
||||
return m_pipeline_object != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void StateTracker::UpdatePipelineLayout()
|
||||
{
|
||||
const bool use_bbox_pipeline_layout = IsSSBODescriptorRequired();
|
||||
VkPipelineLayout pipeline_layout =
|
||||
use_bbox_pipeline_layout ? g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
if (m_pipeline_state.pipeline_layout == pipeline_layout)
|
||||
return;
|
||||
|
||||
// Change the number of active descriptor sets, as well as the pipeline layout
|
||||
m_pipeline_state.pipeline_layout = pipeline_layout;
|
||||
if (use_bbox_pipeline_layout)
|
||||
{
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS;
|
||||
|
||||
// The bbox buffer never changes, so we defer descriptor updates until it is enabled.
|
||||
if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
}
|
||||
|
||||
void StateTracker::UpdatePipelineVertexFormat()
|
||||
{
|
||||
const NativeVertexFormat* vertex_format =
|
||||
m_using_ubershaders ?
|
||||
VertexLoaderManager::GetUberVertexFormat(m_vertex_format->GetVertexDeclaration()) :
|
||||
m_vertex_format;
|
||||
if (m_pipeline_state.vertex_format == vertex_format)
|
||||
return;
|
||||
|
||||
m_pipeline_state.vertex_format = static_cast<const VertexFormat*>(vertex_format);
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateDescriptorSet()
|
||||
{
|
||||
const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO
|
||||
@ -1051,6 +638,9 @@ bool StateTracker::UpdateDescriptorSet()
|
||||
|
||||
for (size_t i = 0; i < NUM_UBO_DESCRIPTOR_SET_BINDINGS; i++)
|
||||
{
|
||||
if (i == UBO_DESCRIPTOR_SET_BINDING_GS && !g_vulkan_context->SupportsGeometryShaders())
|
||||
continue;
|
||||
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
@ -1091,7 +681,7 @@ bool StateTracker::UpdateDescriptorSet()
|
||||
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
}
|
||||
|
||||
if (IsSSBODescriptorRequired() &&
|
||||
if (g_vulkan_context->SupportsBoundingBox() &&
|
||||
(m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
|
||||
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE))
|
||||
{
|
||||
@ -1119,6 +709,7 @@ bool StateTracker::UpdateDescriptorSet()
|
||||
if (num_writes > 0)
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr);
|
||||
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9,19 +9,14 @@
|
||||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/NativeVertexFormat.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class VKPipeline;
|
||||
class StreamBuffer;
|
||||
class VertexFormat;
|
||||
|
||||
@ -35,31 +30,18 @@ public:
|
||||
static bool CreateInstance();
|
||||
static void DestroyInstance();
|
||||
|
||||
const RasterizationState& GetRasterizationState() const
|
||||
{
|
||||
return m_pipeline_state.rasterization_state;
|
||||
}
|
||||
const DepthState& GetDepthStencilState() const { return m_pipeline_state.depth_state; }
|
||||
const BlendingState& GetBlendState() const { return m_pipeline_state.blend_state; }
|
||||
const std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS>& GetPSSamplerBindings() const
|
||||
{
|
||||
return m_bindings.ps_samplers;
|
||||
}
|
||||
VkFramebuffer GetFramebuffer() const { return m_framebuffer; }
|
||||
const VKPipeline* GetPipeline() const { return m_pipeline; }
|
||||
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
|
||||
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
|
||||
|
||||
void SetRenderPass(VkRenderPass load_render_pass, VkRenderPass clear_render_pass);
|
||||
void SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& render_area);
|
||||
void SetVertexFormat(const VertexFormat* vertex_format);
|
||||
|
||||
void SetRasterizationState(const RasterizationState& state);
|
||||
void SetMultisamplingstate(const MultisamplingState& state);
|
||||
void SetDepthState(const DepthState& state);
|
||||
void SetBlendState(const BlendingState& state);
|
||||
|
||||
bool CheckForShaderChanges();
|
||||
void ClearShaders();
|
||||
void SetPipeline(const VKPipeline* pipeline);
|
||||
|
||||
void UpdateVertexShaderConstants();
|
||||
void UpdateGeometryShaderConstants();
|
||||
@ -68,7 +50,6 @@ public:
|
||||
void SetTexture(size_t index, VkImageView view);
|
||||
void SetSampler(size_t index, VkSampler sampler);
|
||||
|
||||
void SetBBoxEnable(bool enable);
|
||||
void SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range);
|
||||
|
||||
void UnbindTexture(VkImageView view);
|
||||
@ -117,30 +98,11 @@ public:
|
||||
|
||||
bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const;
|
||||
|
||||
// Reloads the UID cache, ensuring all pipelines used by the game so far have been created.
|
||||
void ReloadPipelineUIDCache();
|
||||
|
||||
// Clears shader pointers, ensuring that now-deleted modules are not used.
|
||||
void InvalidateShaderPointers();
|
||||
|
||||
private:
|
||||
// Serialized version of PipelineInfo, used when loading/saving the pipeline UID cache.
|
||||
struct SerializedPipelineUID
|
||||
{
|
||||
u32 rasterizer_state_bits;
|
||||
u32 depth_state_bits;
|
||||
u32 blend_state_bits;
|
||||
PortableVertexDeclaration vertex_decl;
|
||||
VertexShaderUid vs_uid;
|
||||
GeometryShaderUid gs_uid;
|
||||
PixelShaderUid ps_uid;
|
||||
};
|
||||
|
||||
// Number of descriptor sets for game draws.
|
||||
enum
|
||||
{
|
||||
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1,
|
||||
NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
|
||||
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
|
||||
};
|
||||
|
||||
enum DITRY_FLAG : u32
|
||||
@ -157,7 +119,6 @@ private:
|
||||
DIRTY_FLAG_SCISSOR = (1 << 9),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 10),
|
||||
DIRTY_FLAG_DESCRIPTOR_SET_BINDING = (1 << 11),
|
||||
DIRTY_FLAG_PIPELINE_BINDING = (1 << 12),
|
||||
|
||||
DIRTY_FLAG_ALL_DESCRIPTOR_SETS = DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO |
|
||||
DIRTY_FLAG_PS_SAMPLERS | DIRTY_FLAG_PS_SSBO
|
||||
@ -165,28 +126,10 @@ private:
|
||||
|
||||
bool Initialize();
|
||||
|
||||
// Appends the specified pipeline info, combined with the UIDs stored in the class.
|
||||
// The info is here so that we can store variations of a UID, e.g. blend state.
|
||||
void AppendToPipelineUIDCache(const PipelineInfo& info);
|
||||
|
||||
// Precaches a pipeline based on the UID information.
|
||||
bool PrecachePipelineUID(const SerializedPipelineUID& uid);
|
||||
|
||||
// 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;
|
||||
|
||||
// Obtains a Vulkan pipeline object for the specified pipeline configuration.
|
||||
// Also adds this pipeline configuration to the UID cache if it is not present already.
|
||||
VkPipeline GetPipelineAndCacheUID();
|
||||
|
||||
// Are bounding box ubershaders enabled? If so, we need to ensure the SSBO is set up,
|
||||
// since the bbox writes are determined by a uniform.
|
||||
bool IsSSBODescriptorRequired() const;
|
||||
|
||||
bool UpdatePipeline();
|
||||
void UpdatePipelineLayout();
|
||||
void UpdatePipelineVertexFormat();
|
||||
bool UpdateDescriptorSet();
|
||||
|
||||
// Allocates storage in the uniform buffer of the specified size. If this storage cannot be
|
||||
@ -205,18 +148,8 @@ private:
|
||||
VkDeviceSize m_index_buffer_offset = 0;
|
||||
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
|
||||
|
||||
// shader state
|
||||
VertexShaderUid m_vs_uid = {};
|
||||
GeometryShaderUid m_gs_uid = {};
|
||||
PixelShaderUid m_ps_uid = {};
|
||||
UberShader::VertexShaderUid m_uber_vs_uid = {};
|
||||
UberShader::PixelShaderUid m_uber_ps_uid = {};
|
||||
bool m_using_ubershaders = false;
|
||||
|
||||
// pipeline state
|
||||
PipelineInfo m_pipeline_state = {};
|
||||
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
|
||||
const VertexFormat* m_vertex_format = nullptr;
|
||||
const VKPipeline* m_pipeline = nullptr;
|
||||
|
||||
// shader bindings
|
||||
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {};
|
||||
@ -230,8 +163,8 @@ private:
|
||||
|
||||
VkDescriptorBufferInfo ps_ssbo = {};
|
||||
} m_bindings;
|
||||
u32 m_num_active_descriptor_sets = 0;
|
||||
size_t m_uniform_buffer_reserve_size = 0;
|
||||
u32 m_num_active_descriptor_sets = 0;
|
||||
|
||||
// rasterization
|
||||
VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
@ -246,18 +179,11 @@ private:
|
||||
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
|
||||
VkRect2D m_framebuffer_size = {};
|
||||
VkRect2D m_framebuffer_render_area = {};
|
||||
bool m_bbox_enabled = false;
|
||||
|
||||
// CPU access tracking
|
||||
u32 m_draw_counter = 0;
|
||||
std::vector<u32> m_cpu_accesses_this_frame;
|
||||
std::vector<u32> m_scheduled_command_buffer_kicks;
|
||||
bool m_allow_background_execution = true;
|
||||
|
||||
// Draw state cache on disk
|
||||
// We don't actually use the value field here, instead we generate the shaders from the uid
|
||||
// on-demand. If all goes well, it should hit the shader and Vulkan pipeline cache, therefore
|
||||
// loading should be reasonably efficient.
|
||||
LinearDiskCache<SerializedPipelineUID, u32> m_uid_cache;
|
||||
};
|
||||
}
|
||||
|
@ -592,16 +592,19 @@ void UtilityShaderDraw::BindDescriptors()
|
||||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
|
||||
set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
UBO_DESCRIPTOR_SET_BINDING_GS,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
UBO_DESCRIPTOR_SET_BINDING_GS,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
}
|
||||
|
||||
set_writes[num_set_writes++] = {
|
||||
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, UBO_DESCRIPTOR_SET_BINDING_PS, 0, 1,
|
||||
|
@ -139,8 +139,6 @@ void VertexManager::vFlush()
|
||||
u32 index_count = IndexGenerator::GetIndexLen();
|
||||
|
||||
// Update tracked state
|
||||
StateTracker::GetInstance()->SetVertexFormat(vertex_format);
|
||||
StateTracker::GetInstance()->CheckForShaderChanges();
|
||||
StateTracker::GetInstance()->UpdateVertexShaderConstants();
|
||||
StateTracker::GetInstance()->UpdateGeometryShaderConstants();
|
||||
StateTracker::GetInstance()->UpdatePixelShaderConstants();
|
||||
@ -165,12 +163,10 @@ void VertexManager::vFlush()
|
||||
bounding_box->Flush();
|
||||
bounding_box->Invalidate();
|
||||
}
|
||||
|
||||
// Update which descriptor set/pipeline layout to use.
|
||||
StateTracker::GetInstance()->SetBBoxEnable(bounding_box_enabled);
|
||||
}
|
||||
|
||||
// Bind all pending state to the command buffer
|
||||
g_renderer->SetPipeline(m_current_pipeline_object);
|
||||
if (!StateTracker::GetInstance()->Bind())
|
||||
{
|
||||
WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count);
|
||||
|
@ -223,6 +223,7 @@ bool VideoBackend::Initialize(void* window_handle)
|
||||
g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
::g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
|
||||
// Invoke init methods on main wrapper classes.
|
||||
@ -230,21 +231,14 @@ bool VideoBackend::Initialize(void* window_handle)
|
||||
// for the remaining classes may call methods on these.
|
||||
if (!StateTracker::CreateInstance() || !FramebufferManager::GetInstance()->Initialize() ||
|
||||
!Renderer::GetInstance()->Initialize() || !VertexManager::GetInstance()->Initialize() ||
|
||||
!TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize())
|
||||
!TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize() ||
|
||||
!::g_shader_cache->Initialize())
|
||||
{
|
||||
PanicAlert("Failed to initialize Vulkan classes.");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure all pipelines previously used by the game have been created.
|
||||
StateTracker::GetInstance()->ReloadPipelineUIDCache();
|
||||
|
||||
// Lastly, precompile ubershaders, if requested.
|
||||
// This has to be done after the texture cache and shader cache are initialized.
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
g_shader_cache->PrecompileUberShaders();
|
||||
|
||||
// Display the name so the user knows which device was actually created.
|
||||
INFO_LOG(VIDEO, "Vulkan Device: %s", g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
return true;
|
||||
@ -252,13 +246,17 @@ bool VideoBackend::Initialize(void* window_handle)
|
||||
|
||||
void VideoBackend::Shutdown()
|
||||
{
|
||||
if (g_renderer)
|
||||
g_renderer->Shutdown();
|
||||
|
||||
if (g_command_buffer_mgr)
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
if (::g_shader_cache)
|
||||
::g_shader_cache->Shutdown();
|
||||
|
||||
if (g_renderer)
|
||||
g_renderer->Shutdown();
|
||||
|
||||
g_perf_query.reset();
|
||||
::g_shader_cache.reset();
|
||||
g_texture_cache.reset();
|
||||
g_vertex_manager.reset();
|
||||
g_renderer.reset();
|
||||
|
Reference in New Issue
Block a user