diff options
| author | Stenzek <stenzek@gmail.com> | 2017-07-20 15:25:35 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2017-07-30 17:43:59 +1000 |
| commit | 81b4ed2a81d7bada71a115efc7fb2347378fb975 (patch) | |
| tree | ba6884e584fc23b872f8b4679cd919085b92ddf9 /Source/Core/VideoBackends/Vulkan/StateTracker.cpp | |
| parent | 4bf562589565636382b81313c2de0482e45743c0 (diff) | |
Vulkan: Uber shader support
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/StateTracker.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/StateTracker.cpp | 273 |
1 files changed, 208 insertions, 65 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index a03437a697..c0661422f1 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -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) + { + // 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()) { - m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid); - m_vs_uid = vs_uid; - changed = true; + // 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); + + // 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(); + 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); - // Add to the UID cache if it is a new pipeline. - if (!result.second && g_ActiveConfig.bShaderCache) - AppendToPipelineUIDCache(info); + return result.first; + } +} - 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 = |
