mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 22:00:39 -06:00
Vulkan: Uber shader support
This commit is contained in:
@ -22,6 +22,7 @@
|
||||
#include "VideoCommon/GeometryShaderManager.h"
|
||||
#include "VideoCommon/PixelShaderManager.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
@ -77,12 +78,13 @@ bool StateTracker::Initialize()
|
||||
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++)
|
||||
{
|
||||
m_bindings.ps_samplers[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
m_bindings.ps_samplers[i].imageView = VK_NULL_HANDLE;
|
||||
m_bindings.ps_samplers[i].imageView = g_object_cache->GetDummyImageView();
|
||||
m_bindings.ps_samplers[i].sampler = g_object_cache->GetPointSampler();
|
||||
}
|
||||
|
||||
@ -178,7 +180,8 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
||||
|
||||
// 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 = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl);
|
||||
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);
|
||||
@ -267,11 +270,11 @@ void StateTracker::SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& ren
|
||||
|
||||
void StateTracker::SetVertexFormat(const VertexFormat* vertex_format)
|
||||
{
|
||||
if (m_pipeline_state.vertex_format == vertex_format)
|
||||
if (m_vertex_format == vertex_format)
|
||||
return;
|
||||
|
||||
m_pipeline_state.vertex_format = vertex_format;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
m_vertex_format = vertex_format;
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
void StateTracker::SetPrimitiveTopology(VkPrimitiveTopology primitive_topology)
|
||||
@ -323,14 +326,94 @@ bool StateTracker::CheckForShaderChanges(u32 gx_primitive_type)
|
||||
{
|
||||
VertexShaderUid vs_uid = GetVertexShaderUid();
|
||||
PixelShaderUid ps_uid = GetPixelShaderUid();
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (vs_uid != m_vs_uid)
|
||||
bool use_ubershaders = g_ActiveConfig.bDisableSpecializedShaders;
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !g_ActiveConfig.bDisableSpecializedShaders)
|
||||
{
|
||||
m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid);
|
||||
m_vs_uid = vs_uid;
|
||||
changed = true;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Ubershader fallback?
|
||||
bool uber_vertex_shader = use_ubershaders || g_ActiveConfig.bForceVertexUberShaders;
|
||||
bool uber_pixel_shader = use_ubershaders || g_ActiveConfig.bForcePixelUberShaders;
|
||||
bool using_ubershaders = uber_vertex_shader || uber_pixel_shader;
|
||||
if (!g_ActiveConfig.CanUseUberShaders())
|
||||
{
|
||||
// Per-pixel lighting disables ubershaders.
|
||||
uber_vertex_shader = false;
|
||||
uber_pixel_shader = false;
|
||||
using_ubershaders = false;
|
||||
}
|
||||
|
||||
// Switching to/from ubershaders? Have to adjust the vertex format and pipeline layout.
|
||||
if (using_ubershaders != m_using_ubershaders)
|
||||
{
|
||||
m_using_ubershaders = using_ubershaders;
|
||||
UpdatePipelineLayout();
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
if (uber_vertex_shader)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (uber_pixel_shader)
|
||||
{
|
||||
UberShader::PixelShaderUid uber_ps_uid = UberShader::GetPixelShaderUid();
|
||||
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())
|
||||
@ -338,29 +421,39 @@ bool StateTracker::CheckForShaderChanges(u32 gx_primitive_type)
|
||||
GeometryShaderUid gs_uid = GetGeometryShaderUid(gx_primitive_type);
|
||||
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);
|
||||
|
||||
m_gs_uid = gs_uid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps_uid != m_ps_uid)
|
||||
{
|
||||
m_pipeline_state.ps = g_shader_cache->GetPixelShaderForUid(ps_uid);
|
||||
m_ps_uid = ps_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;
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::UpdateVertexShaderConstants()
|
||||
{
|
||||
if (!VertexShaderManager::dirty || !ReserveConstantStorage())
|
||||
@ -557,24 +650,8 @@ void StateTracker::SetBBoxEnable(bool enable)
|
||||
if (m_bbox_enabled == enable)
|
||||
return;
|
||||
|
||||
// Change the number of active descriptor sets, as well as the pipeline layout
|
||||
if (enable)
|
||||
{
|
||||
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX);
|
||||
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_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
m_bbox_enabled = enable;
|
||||
UpdatePipelineLayout();
|
||||
}
|
||||
|
||||
void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range)
|
||||
@ -590,7 +667,7 @@ void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceS
|
||||
m_bindings.ps_ssbo.range = range;
|
||||
|
||||
// Defer descriptor update until bbox is actually enabled.
|
||||
if (m_bbox_enabled)
|
||||
if (IsSSBODescriptorRequired())
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
@ -599,7 +676,7 @@ void StateTracker::UnbindTexture(VkImageView view)
|
||||
for (VkDescriptorImageInfo& it : m_bindings.ps_samplers)
|
||||
{
|
||||
if (it.imageView == view)
|
||||
it.imageView = VK_NULL_HANDLE;
|
||||
it.imageView = g_object_cache->GetDummyImageView();
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +686,7 @@ void StateTracker::InvalidateDescriptorSets()
|
||||
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
|
||||
|
||||
// Defer SSBO descriptor update until bbox is actually enabled.
|
||||
if (!m_bbox_enabled)
|
||||
if (!IsSSBODescriptorRequired())
|
||||
m_dirty_flags &= ~DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
@ -886,15 +963,49 @@ void StateTracker::EndClearRenderPass()
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
VkPipeline StateTracker::GetPipelineAndCacheUID(const PipelineInfo& info)
|
||||
VkPipeline StateTracker::GetPipelineAndCacheUID()
|
||||
{
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResult(info);
|
||||
// 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);
|
||||
|
||||
// Add to the UID cache if it is a new pipeline.
|
||||
if (!result.second && g_ActiveConfig.bShaderCache)
|
||||
AppendToPipelineUIDCache(info);
|
||||
// Still waiting for the pipeline to compile?
|
||||
if (!result.first.second)
|
||||
return result.first.first;
|
||||
|
||||
return result.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();
|
||||
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()
|
||||
@ -904,16 +1015,56 @@ bool StateTracker::UpdatePipeline()
|
||||
return false;
|
||||
|
||||
// Grab a new pipeline object, this can fail.
|
||||
m_pipeline_object = GetPipelineAndCacheUID(m_pipeline_state);
|
||||
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
|
||||
NUM_PIXEL_SHADER_SAMPLERS + // Samplers
|
||||
1 + // Samplers
|
||||
1; // SSBO
|
||||
std::array<VkWriteDescriptorSet, MAX_DESCRIPTOR_WRITES> writes;
|
||||
u32 num_writes = 0;
|
||||
@ -954,30 +1105,22 @@ bool StateTracker::UpdateDescriptorSet()
|
||||
if (set == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++)
|
||||
{
|
||||
const VkDescriptorImageInfo& info = m_bindings.ps_samplers[i];
|
||||
if (info.imageView != VK_NULL_HANDLE && info.sampler != VK_NULL_HANDLE)
|
||||
{
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
static_cast<uint32_t>(i),
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
&info,
|
||||
nullptr,
|
||||
nullptr};
|
||||
}
|
||||
}
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
0,
|
||||
0,
|
||||
static_cast<u32>(NUM_PIXEL_SHADER_SAMPLERS),
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
m_bindings.ps_samplers.data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
|
||||
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] = set;
|
||||
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
}
|
||||
|
||||
if (m_bbox_enabled &&
|
||||
(m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
|
||||
if ((m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
|
||||
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE))
|
||||
{
|
||||
VkDescriptorSetLayout layout =
|
||||
|
Reference in New Issue
Block a user