diff --git a/Source/Core/Common/GL/GLUtil.cpp b/Source/Core/Common/GL/GLUtil.cpp index b9f35110ee..02a38d8654 100644 --- a/Source/Core/Common/GL/GLUtil.cpp +++ b/Source/Core/Common/GL/GLUtil.cpp @@ -11,12 +11,14 @@ std::unique_ptr GLInterface; +namespace GLUtil +{ void InitInterface() { GLInterface = HostGL_CreateGLInterface(); } -GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader) +GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader) { // generate objects GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); @@ -100,3 +102,27 @@ GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& return programID; } + +void EnablePrimitiveRestart() +{ + constexpr GLuint PRIMITIVE_RESTART_INDEX = 65535; + + if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3) + { + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + else + { + if (GLExtensions::Version() >= 310) + { + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(PRIMITIVE_RESTART_INDEX); + } + else + { + glEnableClientState(GL_PRIMITIVE_RESTART_NV); + glPrimitiveRestartIndexNV(PRIMITIVE_RESTART_INDEX); + } + } +} +} diff --git a/Source/Core/Common/GL/GLUtil.h b/Source/Core/Common/GL/GLUtil.h index 02f4f18d8a..d4714026f0 100644 --- a/Source/Core/Common/GL/GLUtil.h +++ b/Source/Core/Common/GL/GLUtil.h @@ -8,12 +8,9 @@ #include "Common/GL/GLExtensions/GLExtensions.h" -#ifndef _WIN32 - -#include - -#endif +namespace GLUtil +{ void InitInterface(); - -// Helpers -GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); +GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); +void EnablePrimitiveRestart(); +} diff --git a/Source/Core/Core/Analytics.cpp b/Source/Core/Core/Analytics.cpp index 39f62b3f13..db881d61de 100644 --- a/Source/Core/Core/Analytics.cpp +++ b/Source/Core/Core/Analytics.cpp @@ -180,13 +180,16 @@ void DolphinAnalytics::MakeBaseBuilder() static const char* GetUbershaderMode(const VideoConfig& video_config) { - if (video_config.bDisableSpecializedShaders) + switch (video_config.iUberShaderMode) + { + case UberShaderMode::Exclusive: return "exclusive"; - - if (video_config.bBackgroundShaderCompiling) + case UberShaderMode::Hybrid: return "hybrid"; - - return "disabled"; + case UberShaderMode::Disabled: + default: + return "disabled"; + } } void DolphinAnalytics::MakePerGameBuilder() diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 5f8d4c102a..10a70c6020 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -76,12 +76,10 @@ const ConfigInfo GFX_BACKEND_MULTITHREADING{ const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{ {System::GFX, "Settings", "CommandBufferExecuteInterval"}, 100}; const ConfigInfo GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true}; -const ConfigInfo GFX_BACKGROUND_SHADER_COMPILING{ - {System::GFX, "Settings", "BackgroundShaderCompiling"}, false}; -const ConfigInfo GFX_DISABLE_SPECIALIZED_SHADERS{ - {System::GFX, "Settings", "DisableSpecializedShaders"}, false}; -const ConfigInfo GFX_PRECOMPILE_UBER_SHADERS{ - {System::GFX, "Settings", "PrecompileUberShaders"}, true}; +const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING{ + {System::GFX, "Settings", "WaitForShadersBeforeStarting"}, false}; +const ConfigInfo GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"}, + static_cast(UberShaderMode::Disabled)}; const ConfigInfo GFX_SHADER_COMPILER_THREADS{ {System::GFX, "Settings", "ShaderCompilerThreads"}, 1}; const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS{ diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 70613bee00..5c1684777b 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -59,9 +59,8 @@ extern const ConfigInfo GFX_ENABLE_VALIDATION_LAYER; extern const ConfigInfo GFX_BACKEND_MULTITHREADING; extern const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL; extern const ConfigInfo GFX_SHADER_CACHE; -extern const ConfigInfo GFX_BACKGROUND_SHADER_COMPILING; -extern const ConfigInfo GFX_DISABLE_SPECIALIZED_SHADERS; -extern const ConfigInfo GFX_PRECOMPILE_UBER_SHADERS; +extern const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING; +extern const ConfigInfo GFX_UBERSHADER_MODE; extern const ConfigInfo GFX_SHADER_COMPILER_THREADS; extern const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index d625ba3dce..ef71c085ab 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -46,10 +46,8 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location, Config::GFX_ENABLE_VALIDATION_LAYER.location, Config::GFX_BACKEND_MULTITHREADING.location, Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL.location, Config::GFX_SHADER_CACHE.location, - Config::GFX_BACKGROUND_SHADER_COMPILING.location, - Config::GFX_DISABLE_SPECIALIZED_SHADERS.location, - Config::GFX_PRECOMPILE_UBER_SHADERS.location, Config::GFX_SHADER_COMPILER_THREADS.location, - Config::GFX_SHADER_PRECOMPILER_THREADS.location, + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING.location, Config::GFX_UBERSHADER_MODE.location, + Config::GFX_SHADER_COMPILER_THREADS.location, Config::GFX_SHADER_PRECOMPILER_THREADS.location, Config::GFX_SW_ZCOMPLOC.location, Config::GFX_SW_ZFREEZE.location, Config::GFX_SW_DUMP_OBJECTS.location, Config::GFX_SW_DUMP_TEV_STAGES.location, diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp index 8182e65d85..b6baa9819f 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp @@ -63,9 +63,8 @@ void EnhancementsWidget::CreateWidgets() m_af_combo = new GraphicsChoice({tr("1x"), tr("2x"), tr("4x"), tr("8x"), tr("16x")}, Config::GFX_ENHANCE_MAX_ANISOTROPY); - m_ubershader_combo = new QComboBox; - for (const auto& option : {tr("Disabled"), tr("Hybrid"), tr("Exclusive")}) - m_ubershader_combo->addItem(option); + m_ubershader_combo = new GraphicsChoice({tr("Disabled"), tr("Hybrid"), tr("Exclusive")}, + Config::GFX_UBERSHADER_MODE); m_pp_effect = new QComboBox(); m_configure_pp_effect = new QPushButton(tr("Configure")); @@ -131,9 +130,6 @@ void EnhancementsWidget::ConnectWidgets() { connect(m_aa_combo, static_cast(&QComboBox::currentIndexChanged), [this](int) { SaveSettings(); }); - connect(m_ubershader_combo, - static_cast(&QComboBox::currentIndexChanged), - [this](int) { SaveSettings(); }); connect(m_pp_effect, static_cast(&QComboBox::currentIndexChanged), [this](int) { SaveSettings(); }); connect(m_3d_mode, static_cast(&QComboBox::currentIndexChanged), @@ -156,13 +152,6 @@ void EnhancementsWidget::LoadSettings() QString::fromStdString(std::to_string(aa_selection) + "x " + (ssaa ? "SSAA" : "MSAA"))); m_aa_combo->setEnabled(m_aa_combo->count() > 1); - if (Config::GetBase(Config::GFX_DISABLE_SPECIALIZED_SHADERS)) - m_ubershader_combo->setCurrentIndex(2); - else if (Config::GetBase(Config::GFX_BACKGROUND_SHADER_COMPILING)) - m_ubershader_combo->setCurrentIndex(1); - else - m_ubershader_combo->setCurrentIndex(0); - // Post Processing Shader std::vector shaders = g_Config.stereo_mode == StereoMode::Anaglyph ? @@ -220,10 +209,6 @@ void EnhancementsWidget::SaveSettings() Config::SetBaseOrCurrent(Config::GFX_SSAA, is_ssaa); - int us_value = m_ubershader_combo->currentIndex(); - Config::SetBaseOrCurrent(Config::GFX_BACKGROUND_SHADER_COMPILING, us_value == 1); - Config::SetBaseOrCurrent(Config::GFX_DISABLE_SPECIALIZED_SHADERS, us_value == 2); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, m_pp_effect->currentText().toStdString()); diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp index e76fc6a35c..21281488b8 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp @@ -87,6 +87,8 @@ void GeneralWidget::CreateWidgets() m_keep_window_top = new QCheckBox(tr("Keep Window on Top")); m_hide_cursor = new QCheckBox(tr("Hide Mouse Cursor")); m_render_main_window = new QCheckBox(tr("Render to Main Window")); + m_wait_for_shaders = new GraphicsBool(tr("Immediately Compile Shaders"), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); m_options_box->setLayout(m_options_layout); @@ -101,6 +103,7 @@ void GeneralWidget::CreateWidgets() m_options_layout->addWidget(m_hide_cursor, 3, 0); m_options_layout->addWidget(m_render_main_window, 3, 1); + m_options_layout->addWidget(m_wait_for_shaders, 4, 0); main_layout->addWidget(m_video_box); main_layout->addWidget(m_options_box); @@ -265,6 +268,12 @@ void GeneralWidget::AddDescriptions() static const char* TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION = QT_TR_NOOP("When playing on NetPlay, show chat messages, buffer changes and " "desync alerts.\n\nIf unsure, leave this unchecked."); + static const char* TR_WAIT_FOR_SHADERS_DESCRIPTION = QT_TR_NOOP( + "Waits for all shaders to finish compiling before starting a game. Enabling this " + "option may reduce stuttering or hitching for a short time after the game is " + "started, at the cost of a longer delay before the game starts.\n\nFor systems " + "with two or fewer cores, it is recommended to enable this option, as a large " + "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); AddDescription(m_backend_combo, TR_BACKEND_DESCRIPTION); #ifdef _WIN32 @@ -282,6 +291,7 @@ void GeneralWidget::AddDescriptions() AddDescription(m_show_messages, TR_SHOW_FPS_DESCRIPTION); AddDescription(m_keep_window_top, TR_KEEP_WINDOW_ON_TOP_DESCRIPTION); AddDescription(m_show_messages, TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION); + AddDescription(m_wait_for_shaders, TR_WAIT_FOR_SHADERS_DESCRIPTION); } void GeneralWidget::OnBackendChanged(const QString& backend_name) { diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h index e611950dec..8b2d22f331 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h @@ -52,6 +52,7 @@ private: QCheckBox* m_keep_window_top; QCheckBox* m_hide_cursor; QCheckBox* m_render_main_window; + QCheckBox* m_wait_for_shaders; X11Utils::XRRConfiguration* m_xrr_config; }; diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index a4dfebfbed..f1a57f8b8c 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -317,6 +317,12 @@ static wxString ubershader_desc = "stuttering. Balances performance and smoothness.\n\n" "Exclusive: Ubershaders will always be used. Only recommended for high-end " "systems."); +static wxString wait_for_shaders_desc = + wxTRANSLATE("Waits for all shaders to finish compiling before starting a game. Enabling this " + "option may reduce stuttering or hitching for a short time after the game is " + "started, at the cost of a longer delay before the game starts.\n\nFor systems " + "with two or fewer cores, it is recommended to enable this option, as a large " + "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) : wxDialog(parent, wxID_ANY, wxString::Format(_("Dolphin %s Graphics Configuration"), @@ -442,6 +448,10 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxGetTranslation(backend_multithreading_desc), Config::GFX_BACKEND_MULTITHREADING)); } + + szr_other->Add(CreateCheckBox(page_general, _("Immediately Compile Shaders"), + wxGetTranslation(wait_for_shaders_desc), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING)); } wxStaticBoxSizer* const group_basic = @@ -534,24 +544,13 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) // ubershaders { const std::array mode_choices = {{_("Disabled"), _("Hybrid"), _("Exclusive")}}; - - wxChoice* const choice_mode = - new wxChoice(page_enh, wxID_ANY, wxDefaultPosition, wxDefaultSize, - static_cast(mode_choices.size()), mode_choices.data()); - RegisterControl(choice_mode, wxGetTranslation(ubershader_desc)); szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Ubershaders:")), wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - szr_enh->Add(choice_mode, wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(CreateChoice(page_enh, Config::GFX_UBERSHADER_MODE, + wxGetTranslation(ubershader_desc), mode_choices.size(), + mode_choices.data()), + wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); row += 1; - - // Determine ubershader mode - choice_mode->Bind(wxEVT_CHOICE, &VideoConfigDiag::OnUberShaderModeChanged, this); - if (Config::GetBase(Config::GFX_DISABLE_SPECIALIZED_SHADERS)) - choice_mode->SetSelection(2); - else if (Config::GetBase(Config::GFX_BACKGROUND_SHADER_COMPILING)) - choice_mode->SetSelection(1); - else - choice_mode->SetSelection(0); } // postproc shader @@ -1290,13 +1289,3 @@ void VideoConfigDiag::OnAAChanged(wxCommandEvent& ev) Config::SetBaseOrCurrent(Config::GFX_MSAA, vconfig.backend_info.AAModes[mode]); } - -void VideoConfigDiag::OnUberShaderModeChanged(wxCommandEvent& ev) -{ - // 0: No ubershaders - // 1: Hybrid ubershaders - // 2: Only ubershaders - int mode = ev.GetInt(); - Config::SetBaseOrCurrent(Config::GFX_BACKGROUND_SHADER_COMPILING, mode == 1); - Config::SetBaseOrCurrent(Config::GFX_DISABLE_SPECIALIZED_SHADERS, mode == 2); -} diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index d02d344325..a15472885a 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -139,7 +139,6 @@ protected: void PopulatePostProcessingShaders(); void PopulateAAList(); void OnAAChanged(wxCommandEvent& ev); - void OnUberShaderModeChanged(wxCommandEvent& ev); wxChoice* choice_backend; wxChoice* choice_adapter; diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index b779683fb8..8a2862479b 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -212,6 +212,7 @@ StateCache::~StateCache() ID3D11SamplerState* StateCache::Get(SamplerState state) { + std::lock_guard guard(m_lock); auto it = m_sampler.find(state.hex); if (it != m_sampler.end()) return it->second; @@ -266,6 +267,7 @@ ID3D11SamplerState* StateCache::Get(SamplerState state) ID3D11BlendState* StateCache::Get(BlendingState state) { + std::lock_guard guard(m_lock); auto it = m_blend.find(state.hex); if (it != m_blend.end()) return it->second; @@ -348,6 +350,7 @@ ID3D11BlendState* StateCache::Get(BlendingState state) ID3D11RasterizerState* StateCache::Get(RasterizationState state) { + std::lock_guard guard(m_lock); auto it = m_raster.find(state.hex); if (it != m_raster.end()) return it->second; @@ -372,6 +375,7 @@ ID3D11RasterizerState* StateCache::Get(RasterizationState state) ID3D11DepthStencilState* StateCache::Get(DepthState state) { + std::lock_guard guard(m_lock); auto it = m_depth.find(state.hex); if (it != m_depth.end()) return it->second; diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index 805ed8996b..db48e7f18b 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "Common/BitField.h" @@ -35,6 +36,7 @@ private: std::unordered_map m_raster; std::unordered_map m_blend; std::unordered_map m_sampler; + std::mutex m_lock; }; namespace D3D diff --git a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp index cc8a9ba7bc..9941d3e234 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp @@ -6,7 +6,6 @@ #include "Common/Align.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" @@ -25,16 +24,9 @@ namespace DX11 { -GeometryShaderCache::GSCache GeometryShaderCache::GeometryShaders; -const GeometryShaderCache::GSCacheEntry* GeometryShaderCache::last_entry; -GeometryShaderUid GeometryShaderCache::last_uid; -const GeometryShaderCache::GSCacheEntry GeometryShaderCache::pass_entry; - ID3D11GeometryShader* ClearGeometryShader = nullptr; ID3D11GeometryShader* CopyGeometryShader = nullptr; -LinearDiskCache g_gs_disk_cache; - ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader() { return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? ClearGeometryShader : nullptr; @@ -63,16 +55,6 @@ ID3D11Buffer*& GeometryShaderCache::GetConstantBuffer() return gscbuf; } -// this class will load the precompiled shaders into our cache -class GeometryShaderCacheInserter : public LinearDiskCacheReader -{ -public: - void Read(const GeometryShaderUid& key, const u8* value, u32 value_size) - { - GeometryShaderCache::InsertByteCode(key, value, value_size); - } -}; - const char clear_shader_code[] = { "struct VSOUTPUT\n" "{\n" @@ -155,44 +137,6 @@ void GeometryShaderCache::Init() CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code); CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader"); D3D::SetDebugObjectName(CopyGeometryShader, "copy geometry shader"); - - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileShaders(); -} - -void GeometryShaderCache::LoadShaderCache() -{ - GeometryShaderCacheInserter inserter; - g_gs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "GS", true, true), inserter); -} - -void GeometryShaderCache::Reload() -{ - g_gs_disk_cache.Sync(); - g_gs_disk_cache.Close(); - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileShaders(); -} - -// ONLY to be used during shutdown. -void GeometryShaderCache::Clear() -{ - for (auto& iter : GeometryShaders) - iter.second.Destroy(); - GeometryShaders.clear(); - - last_entry = nullptr; - last_uid = {}; } void GeometryShaderCache::Shutdown() @@ -201,83 +145,5 @@ void GeometryShaderCache::Shutdown() SAFE_RELEASE(ClearGeometryShader); SAFE_RELEASE(CopyGeometryShader); - - Clear(); - g_gs_disk_cache.Sync(); - g_gs_disk_cache.Close(); } - -bool GeometryShaderCache::SetShader(PrimitiveType primitive_type) -{ - GeometryShaderUid uid = GetGeometryShaderUid(primitive_type); - if (last_entry && uid == last_uid) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - D3D::stateman->SetGeometryShader(last_entry->shader); - return true; - } - - // Check if the shader is a pass-through shader - if (uid.GetUidData()->IsPassthrough()) - { - // Return the default pass-through shader - last_uid = uid; - last_entry = &pass_entry; - D3D::stateman->SetGeometryShader(last_entry->shader); - return true; - } - - // Check if the shader is already in the cache - auto iter = GeometryShaders.find(uid); - if (iter != GeometryShaders.end()) - { - const GSCacheEntry& entry = iter->second; - last_uid = uid; - last_entry = &entry; - D3D::stateman->SetGeometryShader(last_entry->shader); - return (entry.shader != nullptr); - } - - // Need to compile a new shader - if (CompileShader(uid)) - return SetShader(primitive_type); - else - return false; -} - -bool GeometryShaderCache::CompileShader(const GeometryShaderUid& uid) -{ - D3DBlob* bytecode; - ShaderCode code = - GenerateGeometryShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (!D3D::CompileGeometryShader(code.GetBuffer(), &bytecode) || - !InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - SAFE_RELEASE(bytecode); - return false; - } - - // Insert the bytecode into the caches - g_gs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - return true; -} - -bool GeometryShaderCache::InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode, - size_t len) -{ - GSCacheEntry& newentry = GeometryShaders[uid]; - newentry.shader = bytecode ? D3D::CreateGeometryShaderFromByteCode(bytecode, len) : nullptr; - return newentry.shader != nullptr; -} - -void GeometryShaderCache::PrecompileShaders() -{ - EnumerateGeometryShaderUids([](const GeometryShaderUid& uid) { - if (GeometryShaders.find(uid) != GeometryShaders.end()) - return; - - CompileShader(uid); - }); -} - } // DX11 diff --git a/Source/Core/VideoBackends/D3D/GeometryShaderCache.h b/Source/Core/VideoBackends/D3D/GeometryShaderCache.h index fdd3d053f2..28c1ee3680 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.h +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.h @@ -15,36 +15,12 @@ class GeometryShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - static bool SetShader(PrimitiveType primitive_type); - static bool CompileShader(const GeometryShaderUid& uid); - static bool InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode, size_t len); - static void PrecompileShaders(); static ID3D11GeometryShader* GetClearGeometryShader(); static ID3D11GeometryShader* GetCopyGeometryShader(); static ID3D11Buffer*& GetConstantBuffer(); - -private: - struct GSCacheEntry - { - ID3D11GeometryShader* shader; - - GSCacheEntry() : shader(nullptr) {} - void Destroy() { SAFE_RELEASE(shader); } - }; - - typedef std::map GSCache; - - static void LoadShaderCache(); - - static GSCache GeometryShaders; - static const GSCacheEntry* last_entry; - static GeometryShaderUid last_uid; - static const GSCacheEntry pass_entry; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp index 525e2b33cb..4ba64eec21 100644 --- a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp @@ -13,6 +13,8 @@ namespace DX11 { +std::mutex s_input_layout_lock; + std::unique_ptr VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { @@ -116,23 +118,34 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl) D3DVertexFormat::~D3DVertexFormat() { - SAFE_RELEASE(m_layout); + ID3D11InputLayout* layout = m_layout.load(); + SAFE_RELEASE(layout); } ID3D11InputLayout* D3DVertexFormat::GetInputLayout(D3DBlob* vs_bytecode) { - if (m_layout) - return m_layout; + // CreateInputLayout requires a shader input, but it only looks at the signature of the shader, + // so we don't need to recompute it if the shader changes. + ID3D11InputLayout* layout = m_layout.load(); + if (layout) + return layout; - // CreateInputLayout requires a shader input, but it only looks at the - // signature of the shader, so we don't need to recompute it if the shader - // changes. HRESULT hr = DX11::D3D::device->CreateInputLayout( - m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &m_layout); + m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &layout); if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); DX11::D3D::SetDebugObjectName(m_layout, "input layout used to emulate the GX pipeline"); - return m_layout; + + // This method can be called from multiple threads, so ensure that only one thread sets the + // cached input layout pointer. If another thread beats this thread, use the existing layout. + ID3D11InputLayout* expected = nullptr; + if (!m_layout.compare_exchange_strong(expected, layout)) + { + SAFE_RELEASE(layout); + layout = expected; + } + + return layout; } } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp index 664ecec3a2..e09e32d8a2 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp @@ -7,7 +7,6 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -27,17 +26,6 @@ namespace DX11 { -PixelShaderCache::PSCache PixelShaderCache::PixelShaders; -PixelShaderCache::UberPSCache PixelShaderCache::UberPixelShaders; -const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry; -const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_uber_entry; -PixelShaderUid PixelShaderCache::last_uid; -UberShader::PixelShaderUid PixelShaderCache::last_uber_uid; - -LinearDiskCache g_ps_disk_cache; -LinearDiskCache g_uber_ps_disk_cache; -extern std::unique_ptr g_async_compiler; - ID3D11PixelShader* s_ColorCopyProgram[2] = {nullptr}; ID3D11PixelShader* s_ClearProgram = nullptr; ID3D11PixelShader* s_AnaglyphProgram = nullptr; @@ -309,17 +297,6 @@ ID3D11Buffer* PixelShaderCache::GetConstantBuffer() return pscbuf; } -// this class will load the precompiled shaders into our cache -template -class PixelShaderCacheInserter : public LinearDiskCacheReader -{ -public: - void Read(const UidType& key, const u8* value, u32 value_size) - { - PixelShaderCache::InsertByteCode(key, value, value_size); - } -}; - void PixelShaderCache::Init() { unsigned int cbsize = Common::AlignUp(static_cast(sizeof(PixelShaderConstants)), @@ -344,58 +321,6 @@ void PixelShaderCache::Init() s_ColorCopyProgram[0] = D3D::CompileAndCreatePixelShader(color_copy_program_code); CHECK(s_ColorCopyProgram[0] != nullptr, "Create color copy pixel shader"); D3D::SetDebugObjectName(s_ColorCopyProgram[0], "color copy pixel shader"); - - Clear(); - - SETSTAT(stats.numPixelShadersCreated, 0); - SETSTAT(stats.numPixelShadersAlive, 0); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -void PixelShaderCache::LoadShaderCache() -{ - PixelShaderCacheInserter inserter; - g_ps_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "PS", true, true), inserter); - - PixelShaderCacheInserter uber_inserter; - g_uber_ps_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "UberPS", false, true), - uber_inserter); -} - -void PixelShaderCache::Reload() -{ - g_ps_disk_cache.Sync(); - g_ps_disk_cache.Close(); - g_uber_ps_disk_cache.Sync(); - g_uber_ps_disk_cache.Close(); - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -// ONLY to be used during shutdown. -void PixelShaderCache::Clear() -{ - for (auto& iter : PixelShaders) - iter.second.Destroy(); - for (auto& iter : UberPixelShaders) - iter.second.Destroy(); - PixelShaders.clear(); - UberPixelShaders.clear(); - - last_entry = nullptr; - last_uber_entry = nullptr; - last_uid = {}; - last_uber_uid = {}; } // Used in Swap() when AA mode has changed @@ -420,255 +345,5 @@ void PixelShaderCache::Shutdown() SAFE_RELEASE(s_rgba6_to_rgb8[i]); SAFE_RELEASE(s_rgb8_to_rgba6[i]); } - - Clear(); - g_ps_disk_cache.Sync(); - g_ps_disk_cache.Close(); - g_uber_ps_disk_cache.Sync(); - g_uber_ps_disk_cache.Close(); } - -bool PixelShaderCache::SetShader() -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(); - - PixelShaderUid uid = GetPixelShaderUid(); - ClearUnusedPixelShaderUidBits(APIType::D3D, &uid); - if (last_entry && uid == last_uid) - { - if (last_entry->pending) - return SetUberShader(); - - if (!last_entry->shader) - return false; - - D3D::stateman->SetPixelShader(last_entry->shader); - return true; - } - - // Check if the shader is already in the cache - auto iter = PixelShaders.find(uid); - if (iter != PixelShaders.end()) - { - const PSCacheEntry& entry = iter->second; - if (entry.pending) - return SetUberShader(); - - last_uid = uid; - last_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - if (!last_entry->shader) - return false; - - D3D::stateman->SetPixelShader(last_entry->shader); - return true; - } - - // Background compiling? - if (g_ActiveConfig.CanBackgroundCompileShaders()) - { - // Create a pending entry - PSCacheEntry entry; - entry.pending = true; - PixelShaders[uid] = entry; - - // Queue normal shader compiling and use ubershader - g_async_compiler->QueueWorkItem( - g_async_compiler->CreateWorkItem(uid)); - return SetUberShader(); - } - - // Need to compile a new shader - D3DBlob* bytecode = nullptr; - ShaderCode code = - GeneratePixelShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - D3D::CompilePixelShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - SAFE_RELEASE(bytecode); - return false; - } - - g_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - return SetShader(); -} - -bool PixelShaderCache::SetUberShader() -{ - UberShader::PixelShaderUid uid = UberShader::GetPixelShaderUid(); - UberShader::ClearUnusedPixelShaderUidBits(APIType::D3D, &uid); - - if (last_uber_entry && last_uber_uid == uid) - { - if (!last_uber_entry->shader) - return false; - - D3D::stateman->SetPixelShader(last_uber_entry->shader); - return true; - } - - auto iter = UberPixelShaders.find(uid); - if (iter != UberPixelShaders.end()) - { - const PSCacheEntry& entry = iter->second; - last_uber_uid = uid; - last_uber_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - if (!last_uber_entry->shader) - return false; - - D3D::stateman->SetPixelShader(last_uber_entry->shader); - return true; - } - - D3DBlob* bytecode = nullptr; - ShaderCode code = - UberShader::GenPixelShader(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - D3D::CompilePixelShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - SAFE_RELEASE(bytecode); - return false; - } - - // Lookup map again. - g_uber_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - bytecode->Release(); - return SetUberShader(); -} - -bool PixelShaderCache::InsertByteCode(const PixelShaderUid& uid, const u8* data, size_t len) -{ - ID3D11PixelShader* shader = data ? D3D::CreatePixelShaderFromByteCode(data, len) : nullptr; - if (!InsertShader(uid, shader)) - { - SAFE_RELEASE(shader); - return false; - } - - return true; -} - -bool PixelShaderCache::InsertByteCode(const UberShader::PixelShaderUid& uid, const u8* data, - size_t len) -{ - ID3D11PixelShader* shader = data ? D3D::CreatePixelShaderFromByteCode(data, len) : nullptr; - if (!InsertShader(uid, shader)) - { - SAFE_RELEASE(shader); - return false; - } - - return true; -} - -bool PixelShaderCache::InsertShader(const PixelShaderUid& uid, ID3D11PixelShader* shader) -{ - auto iter = PixelShaders.find(uid); - if (iter != PixelShaders.end() && !iter->second.pending) - return false; - - PSCacheEntry& newentry = PixelShaders[uid]; - newentry.pending = false; - newentry.shader = shader; - - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, PixelShaders.size()); - return (shader != nullptr); -} - -bool PixelShaderCache::InsertShader(const UberShader::PixelShaderUid& uid, - ID3D11PixelShader* shader) -{ - auto iter = UberPixelShaders.find(uid); - if (iter != UberPixelShaders.end() && !iter->second.pending) - return false; - - PSCacheEntry& newentry = UberPixelShaders[uid]; - newentry.pending = false; - newentry.shader = shader; - return (shader != nullptr); -} - -void PixelShaderCache::QueueUberShaderCompiles() -{ - UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& uid) { - if (UberPixelShaders.find(uid) != UberPixelShaders.end()) - return; - - g_async_compiler->QueueWorkItem( - g_async_compiler->CreateWorkItem(uid)); - }); - - g_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); - }); - g_async_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); -} - -PixelShaderCache::PixelShaderCompilerWorkItem::PixelShaderCompilerWorkItem( - const PixelShaderUid& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(uid)); -} - -PixelShaderCache::PixelShaderCompilerWorkItem::~PixelShaderCompilerWorkItem() -{ - SAFE_RELEASE(m_bytecode); -} - -bool PixelShaderCache::PixelShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - GeneratePixelShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - - if (D3D::CompilePixelShader(code.GetBuffer(), &m_bytecode)) - m_shader = D3D::CreatePixelShaderFromByteCode(m_bytecode); - - return true; -} - -void PixelShaderCache::PixelShaderCompilerWorkItem::Retrieve() -{ - if (InsertShader(m_uid, m_shader)) - g_ps_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); - else - SAFE_RELEASE(m_shader); -} - -PixelShaderCache::UberPixelShaderCompilerWorkItem::UberPixelShaderCompilerWorkItem( - const UberShader::PixelShaderUid& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(uid)); -} - -PixelShaderCache::UberPixelShaderCompilerWorkItem::~UberPixelShaderCompilerWorkItem() -{ - SAFE_RELEASE(m_bytecode); -} - -bool PixelShaderCache::UberPixelShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - UberShader::GenPixelShader(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - - if (D3D::CompilePixelShader(code.GetBuffer(), &m_bytecode)) - m_shader = D3D::CreatePixelShaderFromByteCode(m_bytecode); - - return true; -} - -void PixelShaderCache::UberPixelShaderCompilerWorkItem::Retrieve() -{ - if (InsertShader(m_uid, m_shader)) - g_uber_ps_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); - else - SAFE_RELEASE(m_shader); -} - } // DX11 diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.h b/Source/Core/VideoBackends/D3D/PixelShaderCache.h index 9a0faed9e5..22b6b0cc0c 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.h +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.h @@ -19,16 +19,7 @@ class PixelShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - static bool SetShader(); - static bool SetUberShader(); - static bool InsertByteCode(const PixelShaderUid& uid, const u8* data, size_t len); - static bool InsertByteCode(const UberShader::PixelShaderUid& uid, const u8* data, size_t len); - static bool InsertShader(const PixelShaderUid& uid, ID3D11PixelShader* shader); - static bool InsertShader(const UberShader::PixelShaderUid& uid, ID3D11PixelShader* shader); - static void QueueUberShaderCompiles(); static ID3D11Buffer* GetConstantBuffer(); @@ -40,58 +31,6 @@ public: static ID3D11PixelShader* ReinterpRGB8ToRGBA6(bool multisampled); static void InvalidateMSAAShaders(); - -private: - struct PSCacheEntry - { - ID3D11PixelShader* shader; - bool pending; - - PSCacheEntry() : shader(nullptr), pending(false) {} - void Destroy() { SAFE_RELEASE(shader); } - }; - - class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid); - ~PixelShaderCompilerWorkItem() override; - - bool Compile() override; - void Retrieve() override; - - private: - PixelShaderUid m_uid; - ID3D11PixelShader* m_shader = nullptr; - D3DBlob* m_bytecode = nullptr; - }; - - class UberPixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit UberPixelShaderCompilerWorkItem(const UberShader::PixelShaderUid& uid); - ~UberPixelShaderCompilerWorkItem() override; - - bool Compile() override; - void Retrieve() override; - - private: - UberShader::PixelShaderUid m_uid; - ID3D11PixelShader* m_shader = nullptr; - D3DBlob* m_bytecode = nullptr; - }; - - typedef std::map PSCache; - typedef std::map UberPSCache; - - static void LoadShaderCache(); - - static PSCache PixelShaders; - static UberPSCache UberPixelShaders; - static const PSCacheEntry* last_entry; - static const PSCacheEntry* last_uber_entry; - static PixelShaderUid last_uid; - static UberShader::PixelShaderUid last_uber_uid; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 79fa51a504..91475b89d9 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -71,15 +71,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); - // Setup GX pipeline state - for (auto& sampler : m_gx_state.samplers) - sampler.hex = RenderState::GetPointSamplerState().hex; - - m_gx_state.zmode.testenable = false; - m_gx_state.zmode.updateenable = false; - m_gx_state.zmode.func = ZMode::NEVER; - m_gx_state.raster.cullmode = GenMode::CULL_NONE; - // Clear EFB textures constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), @@ -299,6 +290,8 @@ void Renderer::UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride void Renderer::SetPipeline(const AbstractPipeline* pipeline) { const DXPipeline* dx_pipeline = static_cast(pipeline); + if (!dx_pipeline) + return; D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState()); D3D::stateman->SetDepthState(dx_pipeline->GetDepthState()); @@ -313,11 +306,6 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices, u32 vertex_stride, u32 num_vertices) { - // Textures are fine, they're set directly via SetTexture. - // Since samplers are set via gx_state, we need to fix this up here. - for (size_t stage = 0; stage < m_gx_state.samplers.size(); stage++) - D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage])); - // Copy in uniforms. if (uniforms_size > 0) { @@ -638,11 +626,6 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) RestoreAPIState(); } -void Renderer::SetBlendingState(const BlendingState& state) -{ - m_gx_state.blend.hex = state.hex; -} - // This function has the final picture. We adjust the aspect ratio here. void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks, float Gamma) @@ -683,7 +666,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Enable configuration changes UpdateActiveConfig(); g_texture_cache->OnConfigChanged(g_ActiveConfig); - VertexShaderCache::RetreiveAsyncShaders(); // Flip/present backbuffer to frontbuffer here if (D3D::swapchain) @@ -706,12 +688,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region D3D11_CLEAR_DEPTH, 0.f, 0); } - if (CheckForHostConfigChanges()) - { - VertexShaderCache::Reload(); - GeometryShaderCache::Reload(); - PixelShaderCache::Reload(); - } + CheckForHostConfigChanges(); // begin next frame RestoreAPIState(); @@ -784,30 +761,6 @@ void Renderer::RestoreAPIState() BPFunctions::SetScissor(); } -void Renderer::ApplyState() -{ - D3D::stateman->SetBlendState(m_state_cache.Get(m_gx_state.blend)); - D3D::stateman->SetDepthState(m_state_cache.Get(m_gx_state.zmode)); - D3D::stateman->SetRasterizerState(m_state_cache.Get(m_gx_state.raster)); - D3D::stateman->SetPrimitiveTopology( - StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive)); - FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable); - - for (u32 stage = 0; stage < static_cast(m_gx_state.samplers.size()); stage++) - D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage])); - - ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); - - D3D::stateman->SetPixelConstants(PixelShaderCache::GetConstantBuffer(), - g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr); - D3D::stateman->SetVertexConstants(vertexConstants); - D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer()); -} - -void Renderer::RestoreState() -{ -} - void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer) { const DXFramebuffer* fb = static_cast(framebuffer); @@ -838,16 +791,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, } } -void Renderer::SetRasterizationState(const RasterizationState& state) -{ - m_gx_state.raster.hex = state.hex; -} - -void Renderer::SetDepthState(const DepthState& state) -{ - m_gx_state.zmode.hex = state.hex; -} - void Renderer::SetTexture(u32 index, const AbstractTexture* texture) { D3D::stateman->SetTexture( @@ -857,7 +800,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - m_gx_state.samplers[index].hex = state.hex; + D3D::stateman->SetSampler(index, m_state_cache.Get(state)); } void Renderer::UnbindTexture(const AbstractTexture* texture) diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 7706cbe56d..a7ccb0b9ae 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include "VideoBackends/D3D/D3DState.h" @@ -41,10 +40,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& 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; @@ -54,10 +50,6 @@ public: void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; - // TODO: Fix confusing names (see ResetAPIState and RestoreAPIState) - void ApplyState() override; - void RestoreState() override; - void RenderText(const std::string& text, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; @@ -84,14 +76,6 @@ public: u32 groups_x, u32 groups_y, u32 groups_z) override; private: - struct GXPipelineState - { - std::array samplers; - BlendingState blend; - DepthState zmode; - RasterizationState raster; - }; - void SetupDeviceObjects(); void TeardownDeviceObjects(); void Create3DVisionTexture(int width, int height); @@ -106,7 +90,6 @@ private: void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices); StateCache m_state_cache; - GXPipelineState m_gx_state; std::array m_clear_blend_states{}; std::array m_clear_depth_states{}; diff --git a/Source/Core/VideoBackends/D3D/VertexManager.cpp b/Source/Core/VideoBackends/D3D/VertexManager.cpp index d4aec17bf4..f9861c08a8 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D/VertexManager.cpp @@ -11,6 +11,7 @@ #include "VideoBackends/D3D/BoundingBox.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DState.h" +#include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/GeometryShaderCache.h" #include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoBackends/D3D/Render.h" @@ -135,42 +136,23 @@ void VertexManager::Draw(u32 stride) void VertexManager::vFlush() { - if (!PixelShaderCache::SetShader()) - { - GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); - return; - } - - D3DVertexFormat* vertex_format = - static_cast(VertexLoaderManager::GetCurrentVertexFormat()); - if (!VertexShaderCache::SetShader(vertex_format)) - { - GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); - return; - } - - if (!GeometryShaderCache::SetShader(m_current_primitive_type)) - { - GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); - return; - } - - if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active) - { - D3D::context->OMSetRenderTargetsAndUnorderedAccessViews( - D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, nullptr, nullptr, 2, 1, &BBox::GetUAV(), - nullptr); - } - u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(); - PrepareDrawBuffers(stride); - g_renderer->ApplyState(); + if (!m_current_pipeline_object) + return; + + FramebufferManager::SetIntegerEFBRenderTarget( + m_current_pipeline_config.blending_state.logicopenable); + g_renderer->SetPipeline(m_current_pipeline_object); + + ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); + D3D::stateman->SetPixelConstants(PixelShaderCache::GetConstantBuffer(), + g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr); + D3D::stateman->SetVertexConstants(vertexConstants); + D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer()); Draw(stride); - - g_renderer->RestoreState(); } void VertexManager::ResetBuffer(u32 stride) diff --git a/Source/Core/VideoBackends/D3D/VertexManager.h b/Source/Core/VideoBackends/D3D/VertexManager.h index 5b20cbabaa..c4b45931d2 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.h +++ b/Source/Core/VideoBackends/D3D/VertexManager.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -29,7 +30,7 @@ private: std::array m_elems{}; UINT m_num_elems = 0; - ID3D11InputLayout* m_layout = nullptr; + std::atomic m_layout{nullptr}; }; class VertexManager : public VertexManagerBase diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp index ef40f55a10..63d8b3b6ff 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp @@ -7,7 +7,6 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -28,22 +27,11 @@ namespace DX11 { -VertexShaderCache::VSCache VertexShaderCache::vshaders; -VertexShaderCache::UberVSCache VertexShaderCache::ubervshaders; -const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry; -const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_uber_entry; -VertexShaderUid VertexShaderCache::last_uid; -UberShader::VertexShaderUid VertexShaderCache::last_uber_uid; - static ID3D11VertexShader* SimpleVertexShader = nullptr; static ID3D11VertexShader* ClearVertexShader = nullptr; static ID3D11InputLayout* SimpleLayout = nullptr; static ID3D11InputLayout* ClearLayout = nullptr; -LinearDiskCache g_vs_disk_cache; -LinearDiskCache g_uber_vs_disk_cache; -std::unique_ptr g_async_compiler; - ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() { return SimpleVertexShader; @@ -164,73 +152,12 @@ void VertexShaderCache::Init() D3D::SetDebugObjectName(ClearVertexShader, "clear vertex shader"); D3D::SetDebugObjectName(ClearLayout, "clear input layout"); - Clear(); - SETSTAT(stats.numVertexShadersCreated, 0); SETSTAT(stats.numVertexShadersAlive, 0); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - g_async_compiler = std::make_unique(); - g_async_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ? - g_ActiveConfig.GetShaderPrecompilerThreads() : - g_ActiveConfig.GetShaderCompilerThreads()); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -void VertexShaderCache::LoadShaderCache() -{ - VertexShaderCacheInserter inserter; - g_vs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "VS", true, true), inserter); - - VertexShaderCacheInserter uber_inserter; - g_uber_vs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "UberVS", false, true), - uber_inserter); -} - -void VertexShaderCache::Reload() -{ - g_async_compiler->WaitUntilCompletion(); - g_async_compiler->RetrieveWorkItems(); - - g_vs_disk_cache.Sync(); - g_vs_disk_cache.Close(); - g_uber_vs_disk_cache.Sync(); - g_uber_vs_disk_cache.Close(); - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -void VertexShaderCache::Clear() -{ - for (auto& iter : vshaders) - iter.second.Destroy(); - for (auto& iter : ubervshaders) - iter.second.Destroy(); - vshaders.clear(); - ubervshaders.clear(); - - last_uid = {}; - last_uber_uid = {}; - last_entry = nullptr; - last_uber_entry = nullptr; - last_uid = {}; - last_uber_uid = {}; } void VertexShaderCache::Shutdown() { - g_async_compiler->StopWorkerThreads(); - g_async_compiler->RetrieveWorkItems(); - SAFE_RELEASE(vscbuf); SAFE_RELEASE(SimpleVertexShader); @@ -238,271 +165,5 @@ void VertexShaderCache::Shutdown() SAFE_RELEASE(SimpleLayout); SAFE_RELEASE(ClearLayout); - - Clear(); - g_vs_disk_cache.Sync(); - g_vs_disk_cache.Close(); - g_uber_vs_disk_cache.Sync(); - g_uber_vs_disk_cache.Close(); } - -bool VertexShaderCache::SetShader(D3DVertexFormat* vertex_format) -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(vertex_format); - - VertexShaderUid uid = GetVertexShaderUid(); - if (last_entry && uid == last_uid) - { - if (last_entry->pending) - return SetUberShader(vertex_format); - - if (!last_entry->shader) - return false; - - D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode)); - D3D::stateman->SetVertexShader(last_entry->shader); - return true; - } - - auto iter = vshaders.find(uid); - if (iter != vshaders.end()) - { - const VSCacheEntry& entry = iter->second; - if (entry.pending) - return SetUberShader(vertex_format); - - last_uid = uid; - last_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); - if (!last_entry->shader) - return false; - - D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode)); - D3D::stateman->SetVertexShader(last_entry->shader); - return true; - } - - // Background compiling? - if (g_ActiveConfig.CanBackgroundCompileShaders()) - { - // Create a pending entry - VSCacheEntry entry; - entry.pending = true; - vshaders[uid] = entry; - - // Queue normal shader compiling and use ubershader - g_async_compiler->QueueWorkItem( - g_async_compiler->CreateWorkItem(uid)); - return SetUberShader(vertex_format); - } - - // Need to compile a new shader - D3DBlob* bytecode = nullptr; - ShaderCode code = - GenerateVertexShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - D3D::CompileVertexShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode)) - { - SAFE_RELEASE(bytecode); - return false; - } - - g_vs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - bytecode->Release(); - return SetShader(vertex_format); -} - -bool VertexShaderCache::SetUberShader(D3DVertexFormat* vertex_format) -{ - D3DVertexFormat* uber_vertex_format = static_cast( - VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration())); - UberShader::VertexShaderUid uid = UberShader::GetVertexShaderUid(); - if (last_uber_entry && last_uber_uid == uid) - { - if (!last_uber_entry->shader) - return false; - - D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(last_uber_entry->bytecode)); - D3D::stateman->SetVertexShader(last_uber_entry->shader); - return true; - } - - auto iter = ubervshaders.find(uid); - if (iter != ubervshaders.end()) - { - const VSCacheEntry& entry = iter->second; - last_uber_uid = uid; - last_uber_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); - if (!last_uber_entry->shader) - return false; - - D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(last_uber_entry->bytecode)); - D3D::stateman->SetVertexShader(last_uber_entry->shader); - return true; - } - - // Need to compile a new shader - D3DBlob* bytecode = nullptr; - ShaderCode code = - UberShader::GenVertexShader(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - D3D::CompileVertexShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode)) - { - SAFE_RELEASE(bytecode); - return false; - } - - g_uber_vs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - bytecode->Release(); - return SetUberShader(vertex_format); -} - -bool VertexShaderCache::InsertByteCode(const VertexShaderUid& uid, D3DBlob* blob) -{ - ID3D11VertexShader* shader = - blob ? D3D::CreateVertexShaderFromByteCode(blob->Data(), blob->Size()) : nullptr; - bool result = InsertShader(uid, shader, blob); - SAFE_RELEASE(shader); - return result; -} - -bool VertexShaderCache::InsertByteCode(const UberShader::VertexShaderUid& uid, D3DBlob* blob) -{ - ID3D11VertexShader* shader = - blob ? D3D::CreateVertexShaderFromByteCode(blob->Data(), blob->Size()) : nullptr; - bool result = InsertShader(uid, shader, blob); - SAFE_RELEASE(shader); - return result; -} - -bool VertexShaderCache::InsertShader(const VertexShaderUid& uid, ID3D11VertexShader* shader, - D3DBlob* blob) -{ - auto iter = vshaders.find(uid); - if (iter != vshaders.end() && !iter->second.pending) - return false; - - VSCacheEntry& newentry = vshaders[uid]; - newentry.pending = false; - if (!shader || !blob) - return false; - - shader->AddRef(); - newentry.SetByteCode(blob); - newentry.shader = shader; - - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, static_cast(vshaders.size())); - return true; -} - -bool VertexShaderCache::InsertShader(const UberShader::VertexShaderUid& uid, - ID3D11VertexShader* shader, D3DBlob* blob) -{ - auto iter = ubervshaders.find(uid); - if (iter != ubervshaders.end() && !iter->second.pending) - return false; - - VSCacheEntry& newentry = ubervshaders[uid]; - newentry.pending = false; - if (!shader || !blob) - return false; - - shader->AddRef(); - newentry.SetByteCode(blob); - newentry.shader = shader; - return true; -} - -void VertexShaderCache::RetreiveAsyncShaders() -{ - g_async_compiler->RetrieveWorkItems(); -} - -void VertexShaderCache::QueueUberShaderCompiles() -{ - UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& uid) { - if (ubervshaders.find(uid) != ubervshaders.end()) - return; - - g_async_compiler->QueueWorkItem( - g_async_compiler->CreateWorkItem(uid)); - }); -} - -void VertexShaderCache::WaitForBackgroundCompilesToComplete() -{ - g_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); - }); - g_async_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); - - // Switch from precompile -> runtime compiler threads. - g_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); -} - -VertexShaderCache::VertexShaderCompilerWorkItem::VertexShaderCompilerWorkItem( - const VertexShaderUid& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(uid)); -} - -VertexShaderCache::VertexShaderCompilerWorkItem::~VertexShaderCompilerWorkItem() -{ - SAFE_RELEASE(m_bytecode); - SAFE_RELEASE(m_vs); -} - -bool VertexShaderCache::VertexShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - GenerateVertexShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - - if (D3D::CompileVertexShader(code.GetBuffer(), &m_bytecode)) - m_vs = D3D::CreateVertexShaderFromByteCode(m_bytecode); - - return true; -} - -void VertexShaderCache::VertexShaderCompilerWorkItem::Retrieve() -{ - if (InsertShader(m_uid, m_vs, m_bytecode)) - g_vs_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); -} - -VertexShaderCache::UberVertexShaderCompilerWorkItem::UberVertexShaderCompilerWorkItem( - const UberShader::VertexShaderUid& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(uid)); -} - -VertexShaderCache::UberVertexShaderCompilerWorkItem::~UberVertexShaderCompilerWorkItem() -{ - SAFE_RELEASE(m_bytecode); - SAFE_RELEASE(m_vs); -} - -bool VertexShaderCache::UberVertexShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - UberShader::GenVertexShader(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - - if (D3D::CompileVertexShader(code.GetBuffer(), &m_bytecode)) - m_vs = D3D::CreateVertexShaderFromByteCode(m_bytecode); - - return true; -} - -void VertexShaderCache::UberVertexShaderCompilerWorkItem::Retrieve() -{ - if (InsertShader(m_uid, m_vs, m_bytecode)) - g_uber_vs_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); -} - } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.h b/Source/Core/VideoBackends/D3D/VertexShaderCache.h index aca223ceea..b59d75be47 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.h +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.h @@ -21,14 +21,7 @@ class VertexShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - static bool SetShader(D3DVertexFormat* vertex_format); - static bool SetUberShader(D3DVertexFormat* vertex_format); - static void RetreiveAsyncShaders(); - static void QueueUberShaderCompiles(); - static void WaitForBackgroundCompilesToComplete(); static ID3D11Buffer*& GetConstantBuffer(); @@ -36,76 +29,6 @@ public: static ID3D11VertexShader* GetClearVertexShader(); static ID3D11InputLayout* GetSimpleInputLayout(); static ID3D11InputLayout* GetClearInputLayout(); - - static bool InsertByteCode(const VertexShaderUid& uid, D3DBlob* blob); - static bool InsertByteCode(const UberShader::VertexShaderUid& uid, D3DBlob* blob); - static bool InsertShader(const VertexShaderUid& uid, ID3D11VertexShader* shader, D3DBlob* blob); - static bool InsertShader(const UberShader::VertexShaderUid& uid, ID3D11VertexShader* shader, - D3DBlob* blob); - -private: - struct VSCacheEntry - { - ID3D11VertexShader* shader; - D3DBlob* bytecode; // needed to initialize the input layout - bool pending; - - VSCacheEntry() : shader(nullptr), bytecode(nullptr), pending(false) {} - void SetByteCode(D3DBlob* blob) - { - SAFE_RELEASE(bytecode); - bytecode = blob; - blob->AddRef(); - } - void Destroy() - { - SAFE_RELEASE(shader); - SAFE_RELEASE(bytecode); - } - }; - - class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid); - ~VertexShaderCompilerWorkItem() override; - - bool Compile() override; - void Retrieve() override; - - private: - VertexShaderUid m_uid; - D3DBlob* m_bytecode = nullptr; - ID3D11VertexShader* m_vs = nullptr; - }; - - class UberVertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit UberVertexShaderCompilerWorkItem(const UberShader::VertexShaderUid& uid); - ~UberVertexShaderCompilerWorkItem() override; - - bool Compile() override; - void Retrieve() override; - - private: - UberShader::VertexShaderUid m_uid; - D3DBlob* m_bytecode = nullptr; - ID3D11VertexShader* m_vs = nullptr; - }; - - typedef std::map VSCache; - typedef std::map UberVSCache; - - static void LoadShaderCache(); - static void SetInputLayout(); - - static VSCache vshaders; - static UberVSCache ubervshaders; - static const VSCacheEntry* last_entry; - static const VSCacheEntry* last_uber_entry; - static VertexShaderUid last_uid; - static UberShader::VertexShaderUid last_uber_uid; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 294cd0aebc..fb8c18ae5e 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -21,6 +21,7 @@ #include "VideoBackends/D3D/VertexShaderCache.h" #include "VideoBackends/D3D/VideoBackend.h" +#include "VideoCommon/ShaderCache.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -69,6 +70,7 @@ void VideoBackend::InitBackendInfo() g_Config.backend_info.bSupportsDynamicSamplerIndexing = false; g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = true; IDXGIFactory2* factory; IDXGIAdapter* ad; @@ -148,6 +150,7 @@ bool VideoBackend::Initialize(void* window_handle) // internal interfaces g_renderer = std::make_unique(backbuffer_width, backbuffer_height); + g_shader_cache = std::make_unique(); g_texture_cache = std::make_unique(); g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); @@ -155,7 +158,9 @@ bool VideoBackend::Initialize(void* window_handle) VertexShaderCache::Init(); PixelShaderCache::Init(); GeometryShaderCache::Init(); - VertexShaderCache::WaitForBackgroundCompilesToComplete(); + if (!g_shader_cache->Initialize()) + return false; + D3D::InitUtils(); BBox::Init(); return true; @@ -163,6 +168,7 @@ bool VideoBackend::Initialize(void* window_handle) void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); D3D::ShutdownUtils(); @@ -174,6 +180,7 @@ void VideoBackend::Shutdown() g_perf_query.reset(); g_vertex_manager.reset(); g_texture_cache.reset(); + g_shader_cache.reset(); g_renderer.reset(); ShutdownShared(); diff --git a/Source/Core/VideoBackends/Null/CMakeLists.txt b/Source/Core/VideoBackends/Null/CMakeLists.txt index 82efe38241..b89acd85e8 100644 --- a/Source/Core/VideoBackends/Null/CMakeLists.txt +++ b/Source/Core/VideoBackends/Null/CMakeLists.txt @@ -3,7 +3,6 @@ set(SRCS NullTexture.cpp Render.cpp VertexManager.cpp - ShaderCache.cpp ) set(LIBS diff --git a/Source/Core/VideoBackends/Null/Null.vcxproj b/Source/Core/VideoBackends/Null/Null.vcxproj index 664488035e..22bceda2c2 100644 --- a/Source/Core/VideoBackends/Null/Null.vcxproj +++ b/Source/Core/VideoBackends/Null/Null.vcxproj @@ -39,14 +39,12 @@ - - diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index c687c8f2df..430da35f84 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -9,7 +9,6 @@ #include "VideoBackends/Null/PerfQuery.h" #include "VideoBackends/Null/Render.h" -#include "VideoBackends/Null/ShaderCache.h" #include "VideoBackends/Null/TextureCache.h" #include "VideoBackends/Null/VertexManager.h" #include "VideoBackends/Null/VideoBackend.h" @@ -47,6 +46,7 @@ void VideoBackend::InitBackendInfo() g_Config.backend_info.bSupportsST3CTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = false; // aamodes: We only support 1 sample, so no MSAA g_Config.backend_info.Adapters.clear(); @@ -63,21 +63,15 @@ bool VideoBackend::Initialize(void* window_handle) g_perf_query = std::make_unique(); g_framebuffer_manager = std::make_unique(); g_texture_cache = std::make_unique(); - - VertexShaderCache::s_instance = std::make_unique(); - GeometryShaderCache::s_instance = std::make_unique(); - PixelShaderCache::s_instance = std::make_unique(); - return true; + g_shader_cache = std::make_unique(); + return g_shader_cache->Initialize(); } void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); - PixelShaderCache::s_instance.reset(); - VertexShaderCache::s_instance.reset(); - GeometryShaderCache::s_instance.reset(); - g_texture_cache.reset(); g_perf_query.reset(); g_vertex_manager.reset(); diff --git a/Source/Core/VideoBackends/Null/ShaderCache.cpp b/Source/Core/VideoBackends/Null/ShaderCache.cpp deleted file mode 100644 index 0a5ef30595..0000000000 --- a/Source/Core/VideoBackends/Null/ShaderCache.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "VideoBackends/Null/ShaderCache.h" - -#include "VideoCommon/Debugger.h" -#include "VideoCommon/Statistics.h" -#include "VideoCommon/VideoCommon.h" - -namespace Null -{ -template -ShaderCache::ShaderCache() -{ - Clear(); - - SETSTAT(stats.numPixelShadersCreated, 0); - SETSTAT(stats.numPixelShadersAlive, 0); -} - -template -ShaderCache::~ShaderCache() -{ - Clear(); -} - -template -void ShaderCache::Clear() -{ - m_shaders.clear(); - m_last_entry = nullptr; -} - -template -bool ShaderCache::SetShader(PrimitiveType primitive_type) -{ - Uid uid = GetUid(primitive_type, APIType::OpenGL); - - // Check if the shader is already set - if (m_last_entry) - { - if (uid == m_last_uid) - { - return true; - } - } - - m_last_uid = uid; - - // Check if the shader is already in the cache - auto iter = m_shaders.find(uid); - if (iter != m_shaders.end()) - { - const std::string& entry = iter->second; - m_last_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return true; - } - - // Need to compile a new shader - ShaderCode code = GenerateCode(APIType::OpenGL, uid); - m_shaders.emplace(uid, code.GetBuffer()); - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return true; -} - -template class ShaderCache; -template class ShaderCache; -template class ShaderCache; - -std::unique_ptr VertexShaderCache::s_instance; -std::unique_ptr GeometryShaderCache::s_instance; -std::unique_ptr PixelShaderCache::s_instance; -} diff --git a/Source/Core/VideoBackends/Null/ShaderCache.h b/Source/Core/VideoBackends/Null/ShaderCache.h deleted file mode 100644 index b55185c952..0000000000 --- a/Source/Core/VideoBackends/Null/ShaderCache.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "VideoCommon/GeometryShaderGen.h" -#include "VideoCommon/PixelShaderGen.h" -#include "VideoCommon/VertexShaderGen.h" -#include "VideoCommon/VideoCommon.h" - -namespace Null -{ -template -class ShaderCache -{ -public: - ShaderCache(); - virtual ~ShaderCache(); - - void Clear(); - bool SetShader(PrimitiveType primitive_type); - -protected: - virtual Uid GetUid(PrimitiveType primitive_type, APIType api_type) = 0; - virtual ShaderCode GenerateCode(APIType api_type, Uid uid) = 0; - -private: - std::map m_shaders; - const std::string* m_last_entry = nullptr; - Uid m_last_uid; -}; - -class VertexShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - VertexShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetVertexShaderUid(); - } - ShaderCode GenerateCode(APIType api_type, VertexShaderUid uid) override - { - return GenerateVertexShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -class GeometryShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - GeometryShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetGeometryShaderUid(primitive_type); - } - ShaderCode GenerateCode(APIType api_type, GeometryShaderUid uid) override - { - return GenerateGeometryShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -class PixelShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - PixelShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetPixelShaderUid(); - } - ShaderCode GenerateCode(APIType api_type, PixelShaderUid uid) override - { - return GeneratePixelShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -} // namespace NULL diff --git a/Source/Core/VideoBackends/Null/VertexManager.cpp b/Source/Core/VideoBackends/Null/VertexManager.cpp index 60eaf83aa3..17cab39b05 100644 --- a/Source/Core/VideoBackends/Null/VertexManager.cpp +++ b/Source/Core/VideoBackends/Null/VertexManager.cpp @@ -4,8 +4,6 @@ #include "VideoBackends/Null/VertexManager.h" -#include "VideoBackends/Null/ShaderCache.h" - #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/VertexLoaderManager.h" @@ -41,9 +39,6 @@ void VertexManager::ResetBuffer(u32 stride) void VertexManager::vFlush() { - VertexShaderCache::s_instance->SetShader(m_current_primitive_type); - GeometryShaderCache::s_instance->SetShader(m_current_primitive_type); - PixelShaderCache::s_instance->SetShader(m_current_primitive_type); } } // namespace diff --git a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp index 9d93381d46..bd439898db 100644 --- a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp @@ -46,10 +46,11 @@ OGLPipeline::~OGLPipeline() std::unique_ptr OGLPipeline::Create(const AbstractPipelineConfig& config) { - const PipelineProgram* program = - ProgramShaderCache::GetPipelineProgram(static_cast(config.vertex_shader), - static_cast(config.geometry_shader), - static_cast(config.pixel_shader)); + const PipelineProgram* program = ProgramShaderCache::GetPipelineProgram( + static_cast(config.vertex_format), + static_cast(config.vertex_shader), + static_cast(config.geometry_shader), + static_cast(config.pixel_shader)); if (!program) return nullptr; diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index c2f9ddf468..95d5b0a9cd 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -33,8 +33,6 @@ #include "VideoCommon/ImageWrite.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Statistics.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoCommon.h" @@ -43,8 +41,6 @@ namespace OGL { static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024; -std::unique_ptr - ProgramShaderCache::s_async_compiler; u32 ProgramShaderCache::s_ubo_buffer_size; s32 ProgramShaderCache::s_ubo_align; GLuint ProgramShaderCache::s_attributeless_VBO = 0; @@ -54,17 +50,11 @@ GLuint ProgramShaderCache::s_last_VAO = 0; static std::unique_ptr s_buffer; static int num_failures = 0; -static LinearDiskCache s_program_disk_cache; -static LinearDiskCache s_uber_program_disk_cache; static GLuint CurrentProgram = 0; -ProgramShaderCache::PCache ProgramShaderCache::pshaders; -ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders; -ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms; -ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry; -ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_uber_entry; -SHADERUID ProgramShaderCache::last_uid; -UBERSHADERUID ProgramShaderCache::last_uber_uid; +ProgramShaderCache::PipelineProgramMap ProgramShaderCache::s_pipeline_programs; +std::mutex ProgramShaderCache::s_pipeline_program_lock; static std::string s_glsl_header = ""; +static thread_local bool s_is_shared_context = false; static std::string GetGLSLVersionString() { @@ -97,37 +87,40 @@ static std::string GetGLSLVersionString() void SHADER::SetProgramVariables() { + if (g_ActiveConfig.backend_info.bSupportsBindingLayout) + return; + + // To set uniform blocks/uniforms, the program must be active. We restore the + // current binding at the end of this method to maintain the invariant. + glUseProgram(glprogid); + // Bind UBO and texture samplers - if (!g_ActiveConfig.backend_info.bSupportsBindingLayout) + GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock"); + GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock"); + GLint GSBlock_id = glGetUniformBlockIndex(glprogid, "GSBlock"); + GLint UBERBlock_id = glGetUniformBlockIndex(glprogid, "UBERBlock"); + if (PSBlock_id != -1) + glUniformBlockBinding(glprogid, PSBlock_id, 1); + if (VSBlock_id != -1) + glUniformBlockBinding(glprogid, VSBlock_id, 2); + if (GSBlock_id != -1) + glUniformBlockBinding(glprogid, GSBlock_id, 3); + if (UBERBlock_id != -1) + glUniformBlockBinding(glprogid, UBERBlock_id, 4); + + // Bind Texture Samplers + for (int a = 0; a < 10; ++a) { - // glsl shader must be bind to set samplers if we don't support binding layout - Bind(); + std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a); - GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock"); - GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock"); - GLint GSBlock_id = glGetUniformBlockIndex(glprogid, "GSBlock"); - GLint UBERBlock_id = glGetUniformBlockIndex(glprogid, "UBERBlock"); - - if (PSBlock_id != -1) - glUniformBlockBinding(glprogid, PSBlock_id, 1); - if (VSBlock_id != -1) - glUniformBlockBinding(glprogid, VSBlock_id, 2); - if (GSBlock_id != -1) - glUniformBlockBinding(glprogid, GSBlock_id, 3); - if (UBERBlock_id != -1) - glUniformBlockBinding(glprogid, UBERBlock_id, 4); - - // Bind Texture Samplers - for (int a = 0; a <= 9; ++a) - { - std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a); - - // Still need to get sampler locations since we aren't binding them statically in the shaders - int loc = glGetUniformLocation(glprogid, name.c_str()); - if (loc != -1) - glUniform1i(loc, a); - } + // Still need to get sampler locations since we aren't binding them statically in the shaders + int loc = glGetUniformLocation(glprogid, name.c_str()); + if (loc != -1) + glUniform1i(loc, a); } + + // Restore previous program binding. + glUseProgram(CurrentProgram); } void SHADER::SetProgramBindings(bool is_compute) @@ -266,143 +259,6 @@ void ProgramShaderCache::UploadConstants() } } -SHADER* ProgramShaderCache::SetShader(PrimitiveType primitive_type, - const GLVertexFormat* vertex_format) -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(primitive_type, vertex_format); - - SHADERUID uid; - std::memset(&uid, 0, sizeof(uid)); - uid.puid = GetPixelShaderUid(); - uid.vuid = GetVertexShaderUid(); - uid.guid = GetGeometryShaderUid(primitive_type); - ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid); - - // Check if the shader is already set - if (last_entry && uid == last_uid) - { - last_entry->shader.Bind(); - BindVertexFormat(vertex_format); - return &last_entry->shader; - } - - // Check if shader is already in cache - auto iter = pshaders.find(uid); - if (iter != pshaders.end()) - { - PCacheEntry* entry = &iter->second; - if (entry->pending) - return SetUberShader(primitive_type, vertex_format); - - last_uid = uid; - last_entry = entry; - BindVertexFormat(vertex_format); - last_entry->shader.Bind(); - return &last_entry->shader; - } - - // Compile the new shader program. - PCacheEntry& newentry = pshaders[uid]; - newentry.in_cache = false; - newentry.pending = false; - - // Can we background compile this shader? Requires background shader compiling to be enabled, - // and all ubershaders to have been successfully compiled. - if (g_ActiveConfig.CanBackgroundCompileShaders() && !ubershaders.empty() && s_async_compiler) - { - newentry.pending = true; - s_async_compiler->QueueWorkItem(s_async_compiler->CreateWorkItem(uid)); - return SetUberShader(primitive_type, vertex_format); - } - - // Synchronous shader compiling. - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = GenerateVertexShaderCode(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - - if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - return nullptr; - - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, pshaders.size()); - - last_uid = uid; - last_entry = &newentry; - BindVertexFormat(vertex_format); - last_entry->shader.Bind(); - return &last_entry->shader; -} - -SHADER* ProgramShaderCache::SetUberShader(PrimitiveType primitive_type, - const GLVertexFormat* vertex_format) -{ - UBERSHADERUID uid; - std::memset(&uid, 0, sizeof(uid)); - uid.puid = UberShader::GetPixelShaderUid(); - uid.vuid = UberShader::GetVertexShaderUid(); - uid.guid = GetGeometryShaderUid(primitive_type); - UberShader::ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid); - - // We need to use the ubershader vertex format with all attributes enabled. - // Otherwise, the NV driver can generate variants for the vertex shaders. - const GLVertexFormat* uber_vertex_format = static_cast( - VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration())); - - // Check if the shader is already set - if (last_uber_entry && last_uber_uid == uid) - { - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; - } - - // Check if shader is already in cache - auto iter = ubershaders.find(uid); - if (iter != ubershaders.end()) - { - PCacheEntry* entry = &iter->second; - last_uber_uid = uid; - last_uber_entry = entry; - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; - } - - // Make an entry in the table - PCacheEntry& newentry = ubershaders[uid]; - newentry.in_cache = false; - newentry.pending = false; - - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - { - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - } - - if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); - return nullptr; - } - - last_uber_uid = uid; - last_uber_entry = &newentry; - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; -} - bool ProgramShaderCache::CompileShader(SHADER& shader, const std::string& vcode, const std::string& pcode, const std::string& gcode) { @@ -616,11 +472,6 @@ bool ProgramShaderCache::CheckProgramLinkResult(GLuint id, const std::string& vc return true; } -ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram() -{ - return *last_entry; -} - void ProgramShaderCache::Init() { // We have to get the UBO alignment here because @@ -638,93 +489,14 @@ void ProgramShaderCache::Init() // Then once more to get bytes s_buffer = StreamBuffer::Create(GL_UNIFORM_BUFFER, UBO_LENGTH); - // The GPU shader code appears to be context-specific on Mesa/i965. - // This means that if we compiled the ubershaders asynchronously, they will be recompiled - // on the main thread the first time they are used, causing stutter. Nouveau has been - // reported to crash if draw calls are invoked on the shared context threads. For now, - // disable asynchronous compilation on Mesa. - if (!DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION)) - s_async_compiler = std::make_unique(); - - // Read our shader cache, only if supported and enabled - if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache) - LoadProgramBinaries(); - CreateHeader(); CreateAttributelessVAO(); CurrentProgram = 0; - last_entry = nullptr; - last_uber_entry = nullptr; - - if (g_ActiveConfig.CanPrecompileUberShaders()) - { - if (s_async_compiler) - s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); - PrecompileUberShaders(); - } - - if (s_async_compiler) - { - // No point using the async compiler without workers. - s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); - if (!s_async_compiler->HasWorkerThreads()) - s_async_compiler.reset(); - } -} - -void ProgramShaderCache::RetrieveAsyncShaders() -{ - if (s_async_compiler) - s_async_compiler->RetrieveWorkItems(); -} - -void ProgramShaderCache::Reload() -{ - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion(); - s_async_compiler->RetrieveWorkItems(); - } - - const bool use_cache = g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache; - if (use_cache) - SaveProgramBinaries(); - - s_program_disk_cache.Close(); - s_uber_program_disk_cache.Close(); - DestroyShaders(); - - if (use_cache) - LoadProgramBinaries(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileUberShaders(); - - CurrentProgram = 0; - last_entry = nullptr; - last_uber_entry = nullptr; - last_uid = {}; - last_uber_uid = {}; } void ProgramShaderCache::Shutdown() { - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion(); - s_async_compiler->StopWorkerThreads(); - s_async_compiler->RetrieveWorkItems(); - s_async_compiler.reset(); - } - - // store all shaders in cache on disk - if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache) - SaveProgramBinaries(); - s_program_disk_cache.Close(); - s_uber_program_disk_cache.Close(); - - DestroyShaders(); s_buffer.reset(); glBindVertexArray(0); @@ -735,8 +507,8 @@ void ProgramShaderCache::Shutdown() s_last_VAO = 0; // All pipeline programs should have been released. - _dbg_assert_(VIDEO, pipelineprograms.empty()); - pipelineprograms.clear(); + _dbg_assert_(VIDEO, s_pipeline_programs.empty()); + s_pipeline_programs.clear(); } void ProgramShaderCache::CreateAttributelessVAO() @@ -777,146 +549,28 @@ void ProgramShaderCache::InvalidateLastProgram() CurrentProgram = 0; } -GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size) -{ - const u8* binary = value + sizeof(GLenum); - GLint binary_size = value_size - sizeof(GLenum); - GLenum prog_format; - std::memcpy(&prog_format, value, sizeof(GLenum)); - - GLuint progid = glCreateProgram(); - glProgramBinary(progid, prog_format, binary, binary_size); - - GLint success; - glGetProgramiv(progid, GL_LINK_STATUS, &success); - if (!success) - { - glDeleteProgram(progid); - return 0; - } - - return progid; -} - -bool ProgramShaderCache::CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value, - u32 value_size) -{ - entry->in_cache = true; - entry->pending = false; - entry->shader.glprogid = CreateProgramFromBinary(value, value_size); - if (entry->shader.glprogid == 0) - return false; - - entry->shader.SetProgramVariables(); - return true; -} - -void ProgramShaderCache::LoadProgramBinaries() -{ - GLint Supported; - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &Supported); - if (!Supported) - { - ERROR_LOG(VIDEO, "GL_ARB_get_program_binary is supported, but no binary format is known. So " - "disable shader cache."); - g_ogl_config.bSupportsGLSLCache = false; - } - else - { - // Load game-specific shaders. - std::string cache_filename = - GetDiskShaderCacheFileName(APIType::OpenGL, "ProgramBinaries", true, true); - ProgramShaderCacheInserter inserter(pshaders); - s_program_disk_cache.OpenAndRead(cache_filename, inserter); - - // Load global ubershaders. - cache_filename = - GetDiskShaderCacheFileName(APIType::OpenGL, "UberProgramBinaries", false, true); - ProgramShaderCacheInserter uber_inserter(ubershaders); - s_uber_program_disk_cache.OpenAndRead(cache_filename, uber_inserter); - } - SETSTAT(stats.numPixelShadersAlive, pshaders.size()); -} - -static bool GetProgramBinary(const ProgramShaderCache::PCacheEntry& entry, std::vector& data) -{ - // Clear any prior error code - glGetError(); - - GLint link_status = GL_FALSE, delete_status = GL_TRUE, binary_size = 0; - glGetProgramiv(entry.shader.glprogid, GL_LINK_STATUS, &link_status); - glGetProgramiv(entry.shader.glprogid, GL_DELETE_STATUS, &delete_status); - glGetProgramiv(entry.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size); - if (glGetError() != GL_NO_ERROR || link_status == GL_FALSE || delete_status == GL_TRUE || - binary_size == 0) - { - return false; - } - - data.resize(binary_size + sizeof(GLenum)); - - GLsizei length = binary_size; - GLenum prog_format; - glGetProgramBinary(entry.shader.glprogid, binary_size, &length, &prog_format, - &data[sizeof(GLenum)]); - if (glGetError() != GL_NO_ERROR) - return false; - - std::memcpy(&data[0], &prog_format, sizeof(prog_format)); - return true; -} - -template -static void SaveProgramBinaryMap(CacheMapType& program_map, DiskCacheType& disk_cache) -{ - std::vector binary_data; - for (auto& entry : program_map) - { - if (entry.second.in_cache || entry.second.pending) - continue; - - // Entry is now in cache (even if it fails, we don't want to try to save it again). - entry.second.in_cache = true; - if (!GetProgramBinary(entry.second, binary_data)) - continue; - - disk_cache.Append(entry.first, &binary_data[0], static_cast(binary_data.size())); - } - - disk_cache.Sync(); -} - -void ProgramShaderCache::SaveProgramBinaries() -{ - SaveProgramBinaryMap(pshaders, s_program_disk_cache); - SaveProgramBinaryMap(ubershaders, s_uber_program_disk_cache); -} - -void ProgramShaderCache::DestroyShaders() -{ - glUseProgram(0); - - for (auto& entry : pshaders) - entry.second.Destroy(); - pshaders.clear(); - - for (auto& entry : ubershaders) - entry.second.Destroy(); - ubershaders.clear(); -} - -const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* vertex_shader, +const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* vertex_format, + const OGLShader* vertex_shader, const OGLShader* geometry_shader, const OGLShader* pixel_shader) { PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader}; - auto iter = pipelineprograms.find(key); - if (iter != pipelineprograms.end()) { - iter->second->reference_count++; - return iter->second.get(); + std::lock_guard guard(s_pipeline_program_lock); + auto iter = s_pipeline_programs.find(key); + if (iter != s_pipeline_programs.end()) + { + iter->second->reference_count++; + return iter->second.get(); + } } + // We temporarily change the vertex array to the pipeline's vertex format. + // This can prevent the NVIDIA OpenGL driver from recompiling on first use. + GLuint vao = vertex_format ? vertex_format->VAO : s_attributeless_VAO; + if (s_is_shared_context || vao != s_last_VAO) + glBindVertexArray(vao); + std::unique_ptr prog = std::make_unique(); prog->key = key; @@ -935,25 +589,50 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v // Link program. prog->shader.SetProgramBindings(false); glLinkProgram(prog->shader.glprogid); + + // Restore VAO binding after linking. + if (!s_is_shared_context && vao != s_last_VAO) + glBindVertexArray(s_last_VAO); + if (!ProgramShaderCache::CheckProgramLinkResult(prog->shader.glprogid, {}, {}, {})) { prog->shader.Destroy(); return nullptr; } - auto ip = pipelineprograms.emplace(key, std::move(prog)); + // Lock to insert. A duplicate program may have been created in the meantime. + std::lock_guard guard(s_pipeline_program_lock); + auto iter = s_pipeline_programs.find(key); + if (iter != s_pipeline_programs.end()) + { + // Destroy this program, and use the one which was created first. + prog->shader.Destroy(); + iter->second->reference_count++; + return iter->second.get(); + } + + // Set program variables on the shader which will be returned. + // This is only needed for drivers which don't support binding layout. + prog->shader.SetProgramVariables(); + + // If this is a shared context, ensure we sync before we return the program to + // the main thread. If we don't do this, some driver can lock up (e.g. AMD). + if (s_is_shared_context) + glFinish(); + + auto ip = s_pipeline_programs.emplace(key, std::move(prog)); return ip.first->second.get(); } void ProgramShaderCache::ReleasePipelineProgram(const PipelineProgram* prog) { - auto iter = pipelineprograms.find(prog->key); - _assert_(iter != pipelineprograms.end() && prog == iter->second.get()); + auto iter = s_pipeline_programs.find(prog->key); + _assert_(iter != s_pipeline_programs.end() && prog == iter->second.get()); if (--iter->second->reference_count == 0) { iter->second->shader.Destroy(); - pipelineprograms.erase(iter); + s_pipeline_programs.erase(iter); } } @@ -1124,345 +803,36 @@ void ProgramShaderCache::CreateHeader() v >= GlslEs310 ? "precision highp image2DArray;" : ""); } -void ProgramShaderCache::PrecompileUberShaders() +bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param) { - bool success = true; - - 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; - - UBERSHADERUID uid; - std::memcpy(&uid.vuid, &vuid, sizeof(uid.vuid)); - std::memcpy(&uid.puid, &puid, sizeof(uid.puid)); - std::memcpy(&uid.guid, &guid, sizeof(uid.guid)); - - // The ubershader may already exist if shader caching is enabled. - if (!success || ubershaders.find(uid) != ubershaders.end()) - return; - - PCacheEntry& entry = ubershaders[uid]; - entry.in_cache = false; - entry.pending = false; - - // Multi-context path? - if (s_async_compiler) - { - entry.pending = true; - s_async_compiler->QueueWorkItem( - s_async_compiler->CreateWorkItem(uid)); - return; - } - - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - { - GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - } - - // Always background compile, even when it's not supported. - // This way hopefully the driver can still compile the shaders in parallel. - if (!CompileShader(entry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - { - // Stop compiling shaders if any of them fail, no point continuing. - success = false; - return; - } - }); - }); - }); - - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); - }); - s_async_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); - } - - if (!success) - { - PanicAlert("One or more ubershaders failed to compile. Disabling ubershaders."); - for (auto& it : ubershaders) - it.second.Destroy(); - ubershaders.clear(); - } -} - -bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param) -{ - SharedContextData* ctx_data = new SharedContextData(); - ctx_data->context = GLInterface->CreateSharedContext(); - if (!ctx_data->context) + std::unique_ptr context = GLInterface->CreateSharedContext(); + if (!context) { PanicAlert("Failed to create shared context for shader compiling."); - delete ctx_data; return false; } - *param = ctx_data; + *param = context.release(); return true; } -bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) +bool SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) { - SharedContextData* ctx_data = reinterpret_cast(param); - if (!ctx_data->context->MakeCurrent()) - { - PanicAlert("Failed to make shared context current."); - ctx_data->context->Shutdown(); - delete ctx_data; + cInterfaceBase* context = static_cast(param); + if (!context->MakeCurrent()) return false; - } - CreatePrerenderArrays(ctx_data); - return true; -} - -void ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param) -{ - SharedContextData* ctx_data = reinterpret_cast(param); - DestroyPrerenderArrays(ctx_data); - ctx_data->context->Shutdown(); - delete ctx_data; -} - -ProgramShaderCache::ShaderCompileWorkItem::ShaderCompileWorkItem(const SHADERUID& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(m_uid)); -} - -bool ProgramShaderCache::ShaderCompileWorkItem::Compile() -{ - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - GenerateVertexShaderCode(APIType::OpenGL, host_config, m_uid.vuid.GetUidData()); - ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, m_uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !m_uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); - - CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, - static_cast(m_uid.guid.GetUidData()->primitive_type)); - return true; -} - -void ProgramShaderCache::ShaderCompileWorkItem::Retrieve() -{ - auto iter = pshaders.find(m_uid); - if (iter != pshaders.end() && !iter->second.pending) - { - // Main thread already compiled this shader. - m_program.Destroy(); - return; - } - - PCacheEntry& entry = pshaders[m_uid]; - entry.shader = m_program; - entry.in_cache = false; - entry.pending = false; -} - -ProgramShaderCache::UberShaderCompileWorkItem::UberShaderCompileWorkItem(const UBERSHADERUID& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(m_uid)); -} - -bool ProgramShaderCache::UberShaderCompileWorkItem::Compile() -{ - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, m_uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, m_uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !m_uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); - - CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, - static_cast(m_uid.guid.GetUidData()->primitive_type)); - return true; -} - -void ProgramShaderCache::UberShaderCompileWorkItem::Retrieve() -{ - auto iter = ubershaders.find(m_uid); - if (iter != ubershaders.end() && !iter->second.pending) - { - // Main thread already compiled this shader. - m_program.Destroy(); - return; - } - - PCacheEntry& entry = ubershaders[m_uid]; - entry.shader = m_program; - entry.in_cache = false; - entry.pending = false; -} - -void ProgramShaderCache::CreatePrerenderArrays(SharedContextData* data) -{ - // Create a framebuffer object to render into. - // This is because in EGL, and potentially GLX, we have a surfaceless context. - glGenTextures(1, &data->prerender_FBO_tex); - glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_tex); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glGenTextures(1, &data->prerender_FBO_depth); - glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_depth); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, 1, 1, 1, 0, GL_DEPTH_COMPONENT, - GL_FLOAT, nullptr); - glGenFramebuffers(1, &data->prerender_FBO); - glBindFramebuffer(GL_FRAMEBUFFER, data->prerender_FBO); - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, data->prerender_FBO_tex, 0, 0); - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, data->prerender_FBO_depth, 0, 0); - - // Create VAO for the prerender vertices. - // We don't use the normal VAO map, since we need to change the VBO pointer. - glGenVertexArrays(1, &data->prerender_VAO); - glBindVertexArray(data->prerender_VAO); - - // Create and populate the prerender VBO. We need enough space to draw 3 triangles. - static constexpr float vbo_data[] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - constexpr u32 vbo_stride = sizeof(float) * 3; - glGenBuffers(1, &data->prerender_VBO); - glBindBuffer(GL_ARRAY_BUFFER, data->prerender_VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW); - - // We only need a position in our prerender vertex. - glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); - glVertexAttribPointer(SHADER_POSITION_ATTRIB, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - - // The other attributes have to be active to avoid variant generation. - glEnableVertexAttribArray(SHADER_POSMTX_ATTRIB); - glVertexAttribIPointer(SHADER_POSMTX_ATTRIB, 1, GL_UNSIGNED_BYTE, vbo_stride, nullptr); - for (u32 i = 0; i < 3; i++) - { - glEnableVertexAttribArray(SHADER_NORM0_ATTRIB + i); - glVertexAttribPointer(SHADER_NORM0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - } - for (u32 i = 0; i < 2; i++) - { - glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB + i); - glVertexAttribPointer(SHADER_COLOR0_ATTRIB + i, 4, GL_UNSIGNED_BYTE, GL_TRUE, vbo_stride, - nullptr); - } - for (u32 i = 0; i < 8; i++) - { - glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB + i); - glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - } - - // We need an index buffer to set up the same drawing state on Mesa. - static constexpr u16 ibo_data[] = {0, 1, 2}; - glGenBuffers(1, &data->prerender_IBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data->prerender_IBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ibo_data), ibo_data, GL_STATIC_DRAW); - - // Mesa also requires the primitive restart state matches? + s_is_shared_context = true; if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) - { - if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3) - { - glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); - } - else - { - if (GLExtensions::Version() >= 310) - { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(65535); - } - else - { - glEnableClientState(GL_PRIMITIVE_RESTART_NV); - glPrimitiveRestartIndexNV(65535); - } - } - } + GLUtil::EnablePrimitiveRestart(); + + return true; } -void ProgramShaderCache::DestroyPrerenderArrays(SharedContextData* data) +void SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param) { - if (data->prerender_VAO) - { - glDeleteVertexArrays(1, &data->prerender_VAO); - data->prerender_VAO = 0; - } - if (data->prerender_VBO) - { - glDeleteBuffers(1, &data->prerender_VBO); - data->prerender_VBO = 0; - } - if (data->prerender_IBO) - { - glDeleteBuffers(1, &data->prerender_IBO); - data->prerender_IBO = 0; - } - if (data->prerender_FBO) - { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &data->prerender_FBO); - data->prerender_FBO = 0; - } - if (data->prerender_FBO_tex) - { - glDeleteTextures(1, &data->prerender_FBO_tex); - data->prerender_FBO_tex = 0; - } - if (data->prerender_FBO_depth) - { - glDeleteTextures(1, &data->prerender_FBO_depth); - data->prerender_FBO_depth = 0; - } -} - -void ProgramShaderCache::DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type) -{ - // This is called on a worker thread, so we don't want to use the normal binding process. - glUseProgram(shader.glprogid); - - // The number of primitives drawn depends on the type. - switch (primitive_type) - { - case PrimitiveType::Points: - glDrawElements(GL_POINTS, 1, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::Lines: - glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::Triangles: - glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::TriangleStrip: - glDrawElements(GL_TRIANGLE_STRIP, 3, GL_UNSIGNED_SHORT, nullptr); - break; - } - - // Has to be finished by the time the main thread picks it up. - GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); - glDeleteSync(sync); + cInterfaceBase* context = static_cast(param); + context->ClearCurrent(); + delete context; } } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h index 635c1d3097..0096c6e5b9 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h @@ -6,20 +6,12 @@ #include #include +#include #include #include #include "Common/GL/GLUtil.h" -#include "Common/LinearDiskCache.h" - #include "VideoCommon/AsyncShaderCompiler.h" -#include "VideoCommon/GeometryShaderGen.h" -#include "VideoCommon/PixelShaderGen.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" -#include "VideoCommon/VertexShaderGen.h" - -class cInterfaceBase; namespace OGL { @@ -27,41 +19,6 @@ class OGLShader; class GLVertexFormat; class StreamBuffer; -class SHADERUID -{ -public: - VertexShaderUid vuid; - PixelShaderUid puid; - GeometryShaderUid guid; - - bool operator<(const SHADERUID& r) const - { - return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid); - } - - bool operator==(const SHADERUID& r) const - { - return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid); - } -}; -class UBERSHADERUID -{ -public: - UberShader::VertexShaderUid vuid; - UberShader::PixelShaderUid puid; - GeometryShaderUid guid; - - bool operator<(const UBERSHADERUID& r) const - { - return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid); - } - - bool operator==(const UBERSHADERUID& r) const - { - return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid); - } -}; - struct SHADER { void Destroy() @@ -111,18 +68,6 @@ struct PipelineProgram class ProgramShaderCache { public: - struct PCacheEntry - { - SHADER shader; - bool in_cache; - bool pending; - - void Destroy() { shader.Destroy(); } - }; - - static PCacheEntry GetShaderProgram(); - static SHADER* SetShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format); - static SHADER* SetUberShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format); static void BindVertexFormat(const GLVertexFormat* vertex_format); static void InvalidateVertexFormat(); static void InvalidateLastProgram(); @@ -140,114 +85,25 @@ public: static void UploadConstants(); static void Init(); - static void Reload(); static void Shutdown(); static void CreateHeader(); - static void RetrieveAsyncShaders(); - static void PrecompileUberShaders(); - static const PipelineProgram* GetPipelineProgram(const OGLShader* vertex_shader, + static const PipelineProgram* GetPipelineProgram(const GLVertexFormat* vertex_format, + const OGLShader* vertex_shader, const OGLShader* geometry_shader, const OGLShader* pixel_shader); static void ReleasePipelineProgram(const PipelineProgram* prog); private: - template - class ProgramShaderCacheInserter : public LinearDiskCacheReader - { - public: - ProgramShaderCacheInserter(std::map& shader_map) - : m_shader_map(shader_map) - { - } - - void Read(const UIDType& key, const u8* value, u32 value_size) override - { - if (m_shader_map.find(key) != m_shader_map.end()) - return; - - PCacheEntry& entry = m_shader_map[key]; - if (!CreateCacheEntryFromBinary(&entry, value, value_size)) - { - m_shader_map.erase(key); - return; - } - } - - private: - std::map& m_shader_map; - }; - - class SharedContextAsyncShaderCompiler : public VideoCommon::AsyncShaderCompiler - { - protected: - bool WorkerThreadInitMainThread(void** param) override; - bool WorkerThreadInitWorkerThread(void* param) override; - void WorkerThreadExit(void* param) override; - }; - - struct SharedContextData - { - std::unique_ptr context; - GLuint prerender_FBO; - GLuint prerender_FBO_tex; - GLuint prerender_FBO_depth; - GLuint prerender_VBO; - GLuint prerender_VAO; - GLuint prerender_IBO; - }; - - class ShaderCompileWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit ShaderCompileWorkItem(const SHADERUID& uid); - - bool Compile() override; - void Retrieve() override; - - private: - SHADERUID m_uid; - SHADER m_program; - }; - - class UberShaderCompileWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit UberShaderCompileWorkItem(const UBERSHADERUID& uid); - - bool Compile() override; - void Retrieve() override; - - private: - UBERSHADERUID m_uid; - SHADER m_program; - }; - - typedef std::map PCache; - typedef std::map UberPCache; typedef std::unordered_map, PipelineProgramKeyHash> PipelineProgramMap; static void CreateAttributelessVAO(); - static GLuint CreateProgramFromBinary(const u8* value, u32 value_size); - static bool CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value, u32 value_size); - static void LoadProgramBinaries(); - static void SaveProgramBinaries(); - static void DestroyShaders(); - static void CreatePrerenderArrays(SharedContextData* data); - static void DestroyPrerenderArrays(SharedContextData* data); - static void DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type); - static PCache pshaders; - static UberPCache ubershaders; - static PipelineProgramMap pipelineprograms; - static PCacheEntry* last_entry; - static PCacheEntry* last_uber_entry; - static SHADERUID last_uid; - static UBERSHADERUID last_uber_uid; + static PipelineProgramMap s_pipeline_programs; + static std::mutex s_pipeline_program_lock; - static std::unique_ptr s_async_compiler; static u32 s_ubo_buffer_size; static s32 s_ubo_align; @@ -256,4 +112,12 @@ private: static GLuint s_last_VAO; }; +class SharedContextAsyncShaderCompiler : public VideoCommon::AsyncShaderCompiler +{ +protected: + bool WorkerThreadInitMainThread(void** param) override; + bool WorkerThreadInitWorkerThread(void* param) override; + void WorkerThreadExit(void* param) override; +}; + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index e7c15782ec..8f2ca8a05c 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -81,8 +81,8 @@ static bool s_efbCacheIsCleared = false; static std::vector s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; // 2 for PeekZ and PeekColor -static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const char* message, const void* userParam) +void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const char* message, const void* userParam) { const char* s_source; const char* s_type; @@ -677,6 +677,10 @@ Renderer::Renderer() g_Config.backend_info.bSupportsPaletteConversion && g_Config.backend_info.bSupportsComputeShaders && g_ogl_config.bSupportsImageLoadStore; + // Background compiling is supported only when shared contexts aren't broken. + g_Config.backend_info.bSupportsBackgroundCompiling = + !DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION); + if (g_ogl_config.bSupportsDebug) { if (GLExtensions::Supports("GL_KHR_debug")) @@ -784,25 +788,7 @@ Renderer::Renderer() glClearDepthf(1.0f); if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) - { - if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3) - { - glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); - } - else - { - if (GLExtensions::Version() >= 310) - { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(65535); - } - else - { - glEnableClientState(GL_PRIMITIVE_RESTART_NV); - glPrimitiveRestartIndexNV(65535); - } - } - } + GLUtil::EnablePrimitiveRestart(); IndexGenerator::Init(); UpdateActiveConfig(); @@ -1286,8 +1272,11 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, glClear(clear_mask); } -void Renderer::ApplyBlendingState(const BlendingState& state) +void Renderer::ApplyBlendingState(const BlendingState state, bool force) { + if (!force && m_current_blend_state == state) + return; + bool useDualSource = state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend && (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha); @@ -1360,6 +1349,7 @@ void Renderer::ApplyBlendingState(const BlendingState& state) } glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate); + m_current_blend_state = state; } // This function has the final picture. We adjust the aspect ratio here. @@ -1465,7 +1455,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Clean out old stuff from caches. It's not worth it to clean out the shader caches. g_texture_cache->Cleanup(frameCount); - ProgramShaderCache::RetrieveAsyncShaders(); RestoreAPIState(); @@ -1479,8 +1468,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_sampler_cache->Clear(); // Invalidate shader cache when the host config changes. - if (CheckForHostConfigChanges()) - ProgramShaderCache::Reload(); + CheckForHostConfigChanges(); // For testing zbuffer targets. // Renderer::SetZBufferRender(); @@ -1559,15 +1547,19 @@ void Renderer::RestoreAPIState() glEnable(GL_CLIP_DISTANCE0); glEnable(GL_CLIP_DISTANCE1); } - BPFunctions::SetGenerationMode(); BPFunctions::SetScissor(); BPFunctions::SetViewport(); - BPFunctions::SetDepthMode(); - BPFunctions::SetBlendMode(); + + ApplyRasterizationState(m_current_rasterization_state, true); + ApplyDepthState(m_current_depth_state, true); + ApplyBlendingState(m_current_blend_state, true); } -void Renderer::ApplyRasterizationState(const RasterizationState& state) +void Renderer::ApplyRasterizationState(const RasterizationState state, bool force) { + if (!force && m_current_rasterization_state == state) + return; + // none, ccw, cw, ccw if (state.cullmode != GenMode::CULL_NONE) { @@ -1579,10 +1571,15 @@ void Renderer::ApplyRasterizationState(const RasterizationState& state) { glDisable(GL_CULL_FACE); } + + m_current_rasterization_state = state; } -void Renderer::ApplyDepthState(const DepthState& state) +void Renderer::ApplyDepthState(const DepthState state, bool force) { + if (!force && m_current_depth_state == state) + return; + const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS}; @@ -1600,21 +1597,8 @@ void Renderer::ApplyDepthState(const DepthState& state) glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); } -} -void Renderer::SetRasterizationState(const RasterizationState& state) -{ - ApplyRasterizationState(state); -} - -void Renderer::SetDepthState(const DepthState& state) -{ - ApplyDepthState(state); -} - -void Renderer::SetBlendingState(const BlendingState& state) -{ - ApplyBlendingState(state); + m_current_depth_state = state; } void Renderer::SetPipeline(const AbstractPipeline* pipeline) @@ -1622,6 +1606,9 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) // Not all shader changes currently go through SetPipeline, so we can't // test if the pipeline hasn't changed and skip these applications. Yet. m_graphics_pipeline = static_cast(pipeline); + if (!m_graphics_pipeline) + return; + ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState()); ApplyDepthState(m_graphics_pipeline->GetDepthState()); ApplyBlendingState(m_graphics_pipeline->GetBlendingState()); @@ -1709,4 +1696,9 @@ void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* u glDispatchCompute(groups_x, groups_y, groups_z); ProgramShaderCache::InvalidateLastProgram(); } + +std::unique_ptr Renderer::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index e28f2b52d1..d8882e2111 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -105,10 +105,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& 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; @@ -142,6 +139,8 @@ public: void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z) override; + std::unique_ptr CreateAsyncShaderCompiler() override; + private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const void* data); @@ -155,12 +154,15 @@ private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); - void ApplyBlendingState(const BlendingState& state); - void ApplyRasterizationState(const RasterizationState& state); - void ApplyDepthState(const DepthState& state); + void ApplyBlendingState(const BlendingState state, bool force = false); + void ApplyRasterizationState(const RasterizationState state, bool force = false); + void ApplyDepthState(const DepthState state, bool force = false); void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size); std::array m_bound_textures{}; const OGLPipeline* m_graphics_pipeline = nullptr; + RasterizationState m_current_rasterization_state = {}; + DepthState m_current_depth_state = {}; + BlendingState m_current_blend_state = {}; }; } diff --git a/Source/Core/VideoBackends/OGL/VertexManager.cpp b/Source/Core/VideoBackends/OGL/VertexManager.cpp index f98c72b1ad..00b20e3c76 100644 --- a/Source/Core/VideoBackends/OGL/VertexManager.cpp +++ b/Source/Core/VideoBackends/OGL/VertexManager.cpp @@ -162,8 +162,6 @@ void VertexManager::vFlush() GLVertexFormat* nativeVertexFmt = (GLVertexFormat*)VertexLoaderManager::GetCurrentVertexFormat(); u32 stride = nativeVertexFmt->GetVertexStride(); - ProgramShaderCache::SetShader(m_current_primitive_type, nativeVertexFmt); - PrepareDrawBuffers(stride); // upload global constants @@ -174,7 +172,11 @@ void VertexManager::vFlush() glEnable(GL_STENCIL_TEST); } - Draw(stride); + if (m_current_pipeline_object) + { + g_renderer->SetPipeline(m_current_pipeline_object); + Draw(stride); + } if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation()) { diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 1e88680606..95dfeb23e7 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -160,7 +160,7 @@ bool VideoBackend::Initialize(void* window_handle) InitBackendInfo(); InitializeShared(); - InitInterface(); + GLUtil::InitInterface(); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); if (!GLInterface->Create(window_handle, g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer)) return false; @@ -175,17 +175,20 @@ bool VideoBackend::Initialize(void* window_handle) ProgramShaderCache::Init(); g_texture_cache = std::make_unique(); g_sampler_cache = std::make_unique(); + g_shader_cache = std::make_unique(); static_cast(g_renderer.get())->Init(); TextureConverter::Init(); BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight()); - return true; + return g_shader_cache->Initialize(); } void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); BoundingBox::Shutdown(); TextureConverter::Shutdown(); + g_shader_cache.reset(); g_sampler_cache.reset(); g_texture_cache.reset(); ProgramShaderCache::Shutdown(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index 8027d3fb68..695f58cc1d 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -15,7 +15,7 @@ std::unique_ptr SWOGLWindow::s_instance; void SWOGLWindow::Init(void* window_handle) { - InitInterface(); + GLUtil::InitInterface(); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); if (!GLInterface->Create(window_handle)) { @@ -71,7 +71,7 @@ void SWOGLWindow::Prepare() "#version 300 es\n" "precision highp float;\n"; - m_image_program = OpenGL_CompileProgram(header + vertex_shader, header + frag_shader); + m_image_program = GLUtil::CompileProgram(header + vertex_shader, header + frag_shader); glUseProgram(m_image_program); diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index 20a8958d18..8765a28e4c 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -73,6 +73,7 @@ void VideoSoftware::InitBackendInfo() g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsCopyToVram = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = false; // aamodes g_Config.backend_info.AAModes = {1}; @@ -96,11 +97,15 @@ bool VideoSoftware::Initialize(void* window_handle) g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); g_texture_cache = std::make_unique(); - return true; + g_shader_cache = std::make_unique(); + return g_shader_cache->Initialize(); } void VideoSoftware::Shutdown() { + if (g_shader_cache) + g_shader_cache->Shutdown(); + if (g_renderer) g_renderer->Shutdown(); diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index c6758c6fdc..f2af115b2a 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -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, diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index cbd28b3630..8f5721b4d3 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -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(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(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(ArraySize(standard_sets)), standard_sets, 0, nullptr}, - // BBox - {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, - static_cast(ArraySize(bbox_sets)), bbox_sets, 0, nullptr}, - // Push Constant {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(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(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], diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 588e4b0919..1beb4f3461 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -224,7 +224,7 @@ std::tuple Renderer::UpdateUtilityUniformBuffer(const void* unifo void Renderer::SetPipeline(const AbstractPipeline* pipeline) { - m_graphics_pipeline = static_cast(pipeline); + StateTracker::GetInstance()->SetPipeline(static_cast(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. diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index e62849ac19..b2662a3d4b 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -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& 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; }; } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp index 9f77958b0f..2fa75f4913 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp @@ -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(); - 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 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, 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(info)); - m_pipeline_objects.emplace(info, std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(std::make_pair(static_cast(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 -struct ShaderCacheReader : public LinearDiskCacheReader -{ - ShaderCacheReader(std::map>& 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>& m_shader_map; -}; - -void ShaderCache::LoadShaderCaches() -{ - ShaderCacheReader vs_reader(m_vs_cache.shader_map); - m_vs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "VS", true, true), - vs_reader); - - ShaderCacheReader 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 gs_reader(m_gs_cache.shader_map); - m_gs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "GS", true, true), - gs_reader); - } - - ShaderCacheReader 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 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(m_ps_cache.shader_map.size())); - SETSTAT(stats.numPixelShadersAlive, static_cast(m_ps_cache.shader_map.size())); - SETSTAT(stats.numVertexShadersCreated, static_cast(m_vs_cache.shader_map.size())); - SETSTAT(stats.numVertexShadersAlive, static_cast(m_vs_cache.shader_map.size())); -} - -template -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(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(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(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(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(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(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(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(completed), static_cast(total)); - }); - m_async_shader_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); -} - -void ShaderCache::RetrieveAsyncShaders() -{ - m_async_shader_compiler->RetrieveWorkItems(); -} - -std::pair 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(uid)); - m_vs_cache.shader_map.emplace(uid, - std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(VK_NULL_HANDLE, true); -} - -std::pair 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(uid)); - m_ps_cache.shader_map.emplace(uid, - std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(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(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(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(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(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; -} } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.h b/Source/Core/VideoBackends/Vulkan/ShaderCache.h index 0ad080a3fc..1082d0039e 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.h +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.h @@ -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 GetVertexShaderForUidAsync(const VertexShaderUid& uid); - std::pair 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 GetPipelineWithCacheResult(const PipelineInfo& info); - std::pair, 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 - struct ShaderModuleCache - { - std::map> shader_map; - LinearDiskCache disk_cache; - }; - ShaderModuleCache m_vs_cache; - ShaderModuleCache m_gs_cache; - ShaderModuleCache m_ps_cache; - ShaderModuleCache m_uber_vs_cache; - ShaderModuleCache m_uber_ps_cache; - - std::unordered_map, PipelineInfoHash> - m_pipeline_objects; + std::unordered_map m_pipeline_objects; std::unordered_map 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 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 g_shader_cache; diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index e5be3e2c1e..a3961daf48 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -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 - { - 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(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(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; } diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index 92771504a9..547c62f655 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -9,19 +9,14 @@ #include #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& 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 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 m_cpu_accesses_this_frame; std::vector 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 m_uid_cache; }; } diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 4977a270a3..facae2ac14 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -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, diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp index df245644c4..c6c0aa0c85 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp @@ -14,7 +14,9 @@ namespace Vulkan { -VKPipeline::VKPipeline(VkPipeline pipeline) : m_pipeline(pipeline) +VKPipeline::VKPipeline(VkPipeline pipeline, VkPipelineLayout pipeline_layout, + AbstractPipelineUsage usage) + : m_pipeline(pipeline), m_pipeline_layout(pipeline_layout), m_usage(usage) { } @@ -30,7 +32,8 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con // Get render pass for config. VkRenderPass render_pass = g_object_cache->GetRenderPass( Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.color_texture_format), - VK_FORMAT_UNDEFINED, config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD); + Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.depth_texture_format), + config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD); // Get pipeline layout. VkPipelineLayout pipeline_layout; @@ -68,6 +71,6 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con if (pipeline == VK_NULL_HANDLE) return nullptr; - return std::make_unique(pipeline); + return std::make_unique(pipeline, pipeline_layout, config.usage); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.h b/Source/Core/VideoBackends/Vulkan/VKPipeline.h index ad66313e4c..767215062a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.h +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.h @@ -14,14 +14,19 @@ namespace Vulkan class VKPipeline final : public AbstractPipeline { public: - explicit VKPipeline(VkPipeline pipeline); + explicit VKPipeline(VkPipeline pipeline, VkPipelineLayout pipeline_layout, + AbstractPipelineUsage usage); ~VKPipeline() override; - VkPipeline GetPipeline() const { return m_pipeline; } + VkPipeline GetVkPipeline() const { return m_pipeline; } + VkPipelineLayout GetVkPipelineLayout() const { return m_pipeline_layout; } + AbstractPipelineUsage GetUsage() const { return m_usage; } static std::unique_ptr Create(const AbstractPipelineConfig& config); private: VkPipeline m_pipeline; + VkPipelineLayout m_pipeline_layout; + AbstractPipelineUsage m_usage; }; } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.cpp b/Source/Core/VideoBackends/Vulkan/VKShader.cpp index 59128de6b2..ed723235db 100644 --- a/Source/Core/VideoBackends/Vulkan/VKShader.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKShader.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Align.h" #include "Common/Assert.h" #include "VideoBackends/Vulkan/ShaderCompiler.h" @@ -103,20 +104,11 @@ std::unique_ptr VKShader::CreateFromSource(ShaderStage stage, const ch std::unique_ptr VKShader::CreateFromBinary(ShaderStage stage, const void* data, size_t length) { - ShaderCompiler::SPIRVCodeVector spv; - const size_t size_in_words = sizeof(length) / sizeof(ShaderCompiler::SPIRVCodeType); - if (size_in_words > 0) - { - spv.resize(length / size_in_words); - std::memcpy(spv.data(), data, size_in_words); - } - - // Non-aligned code sizes, unlikely (unless using VK_NV_glsl). - if ((length % sizeof(ShaderCompiler::SPIRVCodeType)) != 0) - { - spv.resize(size_in_words + 1); - std::memcpy(&spv[size_in_words], data, (length % sizeof(ShaderCompiler::SPIRVCodeType))); - } + const size_t size_in_words = Common::AlignUp(length, sizeof(ShaderCompiler::SPIRVCodeType)) / + sizeof(ShaderCompiler::SPIRVCodeType); + ShaderCompiler::SPIRVCodeVector spv(size_in_words); + if (length > 0) + std::memcpy(spv.data(), data, length); return CreateShaderObject(stage, std::move(spv)); } diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp index c9aad0254c..1e97cf6308 100644 --- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp @@ -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); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 77a7a8ffe1..f9c254a04d 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -235,6 +235,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config) config->backend_info.bSupportsBitfield = true; // Assumed support. config->backend_info.bSupportsDynamicSamplerIndexing = true; // Assumed support. config->backend_info.bSupportsPostProcessing = true; // Assumed support. + config->backend_info.bSupportsBackgroundCompiling = true; // Assumed support. config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features. config->backend_info.bSupportsGeometryShaders = false; // Dependent on features. config->backend_info.bSupportsGSInstancing = false; // Dependent on features. diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index d8a725845a..9bd4039138 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -223,6 +223,7 @@ bool VideoBackend::Initialize(void* window_handle) g_renderer = std::make_unique(std::move(swap_chain)); g_vertex_manager = std::make_unique(); g_texture_cache = std::make_unique(); + ::g_shader_cache = std::make_unique(); g_perf_query = std::make_unique(); // 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(); diff --git a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp index 59ef9762f1..e605828ed5 100644 --- a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp +++ b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp @@ -18,7 +18,6 @@ AsyncShaderCompiler::~AsyncShaderCompiler() // Pending work can be left at shutdown. // The work item classes are expected to clean up after themselves. _assert_(!HasWorkerThreads()); - _assert_(m_completed_work.empty()); } void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item) diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 0e4551da09..61e4c54b27 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -28,9 +28,7 @@ void FlushPipeline() void SetGenerationMode() { - RasterizationState state = {}; - state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType()); - g_renderer->SetRasterizationState(state); + g_vertex_manager->SetRasterizationStateChanged(); } void SetScissor() @@ -129,16 +127,12 @@ void SetViewport() void SetDepthMode() { - DepthState state = {}; - state.Generate(bpmem); - g_renderer->SetDepthState(state); + g_vertex_manager->SetDepthStateChanged(); } void SetBlendMode() { - BlendingState state = {}; - state.Generate(bpmem); - g_renderer->SetBlendingState(state); + g_vertex_manager->SetBlendingStateChanged(); } /* Explanation of the magic behind ClearScreen: diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 42de08e9f1..22ed8519d5 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRCS PostProcessing.cpp RenderBase.cpp RenderState.cpp + ShaderCache.cpp ShaderGenCommon.cpp Statistics.cpp UberShaderCommon.cpp diff --git a/Source/Core/VideoCommon/DriverDetails.cpp b/Source/Core/VideoCommon/DriverDetails.cpp index d280a6a9b4..8fd990395d 100644 --- a/Source/Core/VideoCommon/DriverDetails.cpp +++ b/Source/Core/VideoCommon/DriverDetails.cpp @@ -102,8 +102,6 @@ static BugInfo m_known_bugs[] = { true}, {API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN, BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true}, - {API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_NOUVEAU, Family::UNKNOWN, - BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true}, {API_VULKAN, OS_ALL, VENDOR_NVIDIA, DRIVER_NVIDIA, Family::UNKNOWN, BUG_BROKEN_MSAA_CLEAR, -1.0, -1.0, true}, {API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN, diff --git a/Source/Core/VideoCommon/DriverDetails.h b/Source/Core/VideoCommon/DriverDetails.h index df06a77d92..bc96ee2aab 100644 --- a/Source/Core/VideoCommon/DriverDetails.h +++ b/Source/Core/VideoCommon/DriverDetails.h @@ -252,8 +252,10 @@ enum Bug // the negated value to a temporary variable then using that in the bitwise op. BUG_BROKEN_BITWISE_OP_NEGATION, - // Bug: Shaders are recompiled on the main thread after being previously compiled on - // a worker thread on Mesa i965. + // BUG: The GPU shader code appears to be context-specific on Mesa/i965. + // This means that if we compiled the ubershaders asynchronously, they will be recompiled + // on the main thread the first time they are used, causing stutter. For now, disable + // asynchronous compilation on Mesa i965. // Started version: -1 // Ended Version: -1 BUG_SHARED_CONTEXT_SHADER_COMPILATION, diff --git a/Source/Core/VideoCommon/GXPipelineTypes.h b/Source/Core/VideoCommon/GXPipelineTypes.h new file mode 100644 index 0000000000..165e9c67f8 --- /dev/null +++ b/Source/Core/VideoCommon/GXPipelineTypes.h @@ -0,0 +1,75 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "VideoCommon/GeometryShaderGen.h" +#include "VideoCommon/PixelShaderGen.h" +#include "VideoCommon/RenderState.h" +#include "VideoCommon/UberShaderPixel.h" +#include "VideoCommon/UberShaderVertex.h" +#include "VideoCommon/VertexShaderGen.h" + +class NativeVertexFormat; + +namespace VideoCommon +{ +struct GXPipelineUid +{ + const NativeVertexFormat* vertex_format; + VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + // We use memcmp() for comparing pipelines as std::tie generates a large number of instructions, + // and this map lookup can happen every draw call. However, as using memcmp() will also compare + // any padding bytes, we have to ensure these are zeroed out. + GXPipelineUid() { std::memset(this, 0, sizeof(*this)); } + GXPipelineUid(const GXPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXPipelineUid& operator=(const GXPipelineUid& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } + bool operator<(const GXPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) < 0; + } + bool operator==(const GXPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) == 0; + } + bool operator!=(const GXPipelineUid& rhs) const { return !operator==(rhs); } +}; +struct GXUberPipelineUid +{ + const NativeVertexFormat* vertex_format; + UberShader::VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + UberShader::PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + GXUberPipelineUid() { std::memset(this, 0, sizeof(*this)); } + GXUberPipelineUid(const GXUberPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXUberPipelineUid& operator=(const GXUberPipelineUid& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } + bool operator<(const GXUberPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) < 0; + } + bool operator==(const GXUberPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) == 0; + } + bool operator!=(const GXUberPipelineUid& rhs) const { return !operator==(rhs); } +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h index ace05baca7..0c688d30a7 100644 --- a/Source/Core/VideoCommon/GeometryShaderGen.h +++ b/Source/Core/VideoCommon/GeometryShaderGen.h @@ -8,7 +8,6 @@ #include "Common/CommonTypes.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/ShaderGenCommon.h" -#include "VideoCommon/VertexManagerBase.h" enum class APIType; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 8aea2f098d..a5e5272651 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -56,6 +56,7 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/PostProcessing.h" +#include "VideoCommon/ShaderCache.h" #include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TextureCacheBase.h" @@ -92,6 +93,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) m_surface_handle = Host_GetRenderHandle(); m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; + m_last_efb_multisamples = g_ActiveConfig.iMultisamples; } Renderer::~Renderer() = default; @@ -234,11 +236,20 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet bool Renderer::CheckForHostConfigChanges() { ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); - if (new_host_config.bits == m_last_host_config_bits) + if (new_host_config.bits == m_last_host_config_bits && + m_last_efb_multisamples == g_ActiveConfig.iMultisamples) + { return false; + } - OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); m_last_host_config_bits = new_host_config.bits; + m_last_efb_multisamples = g_ActiveConfig.iMultisamples; + + // Reload shaders. + OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); + SetPipeline(nullptr); + g_vertex_manager->InvalidatePipelineObject(); + g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); return true; } @@ -688,6 +699,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // Set default viewport and scissor, for the clear to work correctly // New frame stats.ResetFrame(); + g_shader_cache->RetrieveAsyncShaders(); + + // We invalidate the pipeline object at the start of the frame. + // This is for the rare case where only a single pipeline configuration is used, + // and hybrid ubershaders have compiled the specialized shader, but without any + // state changes the specialized shader will not take over. + g_vertex_manager->InvalidatePipelineObject(); Core::Callback_VideoCopiedToXFB(true); } @@ -1009,3 +1027,8 @@ bool Renderer::UseVertexDepthRange() const // in the vertex shader. return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; } + +std::unique_ptr Renderer::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index a51f1b2813..e7f7668f22 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -28,6 +28,7 @@ #include "Common/Flag.h" #include "Common/MathUtil.h" #include "VideoCommon/AVIDump.h" +#include "VideoCommon/AsyncShaderCompiler.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/FPSCounter.h" #include "VideoCommon/RenderState.h" @@ -78,10 +79,7 @@ public: }; virtual void SetPipeline(const AbstractPipeline* pipeline) {} - virtual void SetBlendingState(const BlendingState& state) {} virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} - virtual void SetRasterizationState(const RasterizationState& state) {} - virtual void SetDepthState(const DepthState& state) {} virtual void SetTexture(u32 index, const AbstractTexture* texture) {} virtual void SetSamplerState(u32 index, const SamplerState& state) {} virtual void UnbindTexture(const AbstractTexture* texture) {} @@ -189,6 +187,8 @@ public: void ResizeSurface(int new_width, int new_height); bool UseVertexDepthRange() const; + virtual std::unique_ptr CreateAsyncShaderCompiler(); + virtual void Shutdown(); // Drawing utility shaders. @@ -243,6 +243,7 @@ protected: std::mutex m_swap_mutex; u32 m_last_host_config_bits = 0; + u32 m_last_efb_multisamples = 1; private: void RunFrameDumps(); diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp new file mode 100644 index 0000000000..512adbd12a --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -0,0 +1,922 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/ShaderCache.h" + +#include "Common/Assert.h" +#include "Common/MsgHandler.h" +#include "Core/Host.h" + +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Statistics.h" +#include "VideoCommon/VertexLoaderManager.h" +#include "VideoCommon/VertexManagerBase.h" + +std::unique_ptr g_shader_cache; + +namespace VideoCommon +{ +ShaderCache::ShaderCache() = default; +ShaderCache::~ShaderCache() = default; + +bool ShaderCache::Initialize() +{ + m_api_type = g_ActiveConfig.backend_info.api_type; + m_host_config = ShaderHostConfig::GetCurrent(); + m_efb_multisamples = g_ActiveConfig.iMultisamples; + + // Create the async compiler, and start the worker threads. + m_async_shader_compiler = g_renderer->CreateAsyncShaderCompiler(); + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); + + // Load shader and UID caches. + if (g_ActiveConfig.bShaderCache) + { + LoadShaderCaches(); + LoadPipelineUIDCache(); + } + + // Queue ubershader precompiling if required. + if (g_ActiveConfig.UsingUberShaders()) + PrecompileUberShaders(); + + // Compile all known UIDs. + CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); + + // Switch to the runtime shader compiler thread configuration. + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); + return true; +} + +void ShaderCache::SetHostConfig(const ShaderHostConfig& host_config, u32 efb_multisamples) +{ + if (m_host_config.bits == host_config.bits && m_efb_multisamples == efb_multisamples) + return; + + m_host_config = host_config; + m_efb_multisamples = efb_multisamples; + Reload(); +} + +void ShaderCache::Reload() +{ + WaitForAsyncCompiler(); + InvalidateCachedPipelines(); + ClearShaderCaches(); + + if (g_ActiveConfig.bShaderCache) + LoadShaderCaches(); + + // Switch to the precompiling shader configuration while we rebuild. + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); + + // We don't need to explicitly recompile the individual ubershaders here, as the pipelines + // UIDs are still be in the map. Therefore, when these are rebuilt, the shaders will also + // be recompiled. + CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); +} + +void ShaderCache::RetrieveAsyncShaders() +{ + m_async_shader_compiler->RetrieveWorkItems(); +} + +void ShaderCache::Shutdown() +{ + // This may leave shaders uncommitted to the cache, but it's better than blocking shutdown + // until everything has finished compiling. + m_async_shader_compiler->StopWorkerThreads(); + ClearShaderCaches(); + ClearPipelineCaches(); +} + +const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineUid& uid) +{ + auto it = m_gx_pipeline_cache.find(uid); + if (it != m_gx_pipeline_cache.end() && !it->second.second) + return it->second.first.get(); + + std::unique_ptr pipeline; + std::optional pipeline_config = GetGXPipelineConfig(uid); + if (pipeline_config) + pipeline = g_renderer->CreatePipeline(*pipeline_config); + if (g_ActiveConfig.bShaderCache) + AppendGXPipelineUID(uid); + return InsertGXPipeline(uid, std::move(pipeline)); +} + +std::optional ShaderCache::GetPipelineForUidAsync(const GXPipelineUid& uid) +{ + auto it = m_gx_pipeline_cache.find(uid); + if (it != m_gx_pipeline_cache.end()) + { + if (!it->second.second) + return it->second.first.get(); + else + return {}; + } + + auto vs_iter = m_vs_cache.shader_map.find(uid.vs_uid); + if (vs_iter == m_vs_cache.shader_map.end()) + { + QueueVertexShaderCompile(uid.vs_uid); + return {}; + } + else if (vs_iter->second.pending) + { + // VS is still compiling. + return {}; + } + + auto ps_iter = m_ps_cache.shader_map.find(uid.ps_uid); + if (ps_iter == m_ps_cache.shader_map.end()) + { + QueuePixelShaderCompile(uid.ps_uid); + return {}; + } + else if (ps_iter->second.pending) + { + // PS is still compiling. + return {}; + } + + if (NeedsGeometryShader(uid.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(uid.gs_uid); + if (gs_iter == m_gs_cache.shader_map.end()) + CreateGeometryShader(uid.gs_uid); + } + + // All shader stages are present, queue the pipeline compile. + if (g_ActiveConfig.bShaderCache) + AppendGXPipelineUID(uid); + QueuePipelineCompile(uid); + return {}; +} + +const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineUid& uid) +{ + auto it = m_gx_uber_pipeline_cache.find(uid); + if (it != m_gx_uber_pipeline_cache.end() && !it->second.second) + return it->second.first.get(); + + std::unique_ptr pipeline; + std::optional pipeline_config = GetGXUberPipelineConfig(uid); + if (pipeline_config) + pipeline = g_renderer->CreatePipeline(*pipeline_config); + return InsertGXUberPipeline(uid, std::move(pipeline)); +} + +void ShaderCache::WaitForAsyncCompiler() +{ + while (m_async_shader_compiler->HasPendingWork()) + { + m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) { + Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), + static_cast(completed), static_cast(total)); + }); + m_async_shader_compiler->RetrieveWorkItems(); + } + Host_UpdateProgressDialog("", -1, -1); +} + +template +static void LoadShaderCache(T& cache, APIType api_type, const char* type, bool include_gameid) +{ + class CacheReader : public LinearDiskCacheReader + { + public: + CacheReader(T& cache_) : cache(cache_) {} + void Read(const K& key, const u8* value, u32 value_size) + { + auto shader = g_renderer->CreateShaderFromBinary(stage, value, value_size); + if (shader) + { + auto& entry = cache.shader_map[key]; + entry.shader = std::move(shader); + entry.pending = false; + + switch (stage) + { + case ShaderStage::Vertex: + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + break; + case ShaderStage::Pixel: + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + break; + default: + break; + } + } + } + + private: + T& cache; + }; + + std::string filename = GetDiskShaderCacheFileName(api_type, type, include_gameid, true); + CacheReader reader(cache); + u32 count = cache.disk_cache.OpenAndRead(filename, reader); + INFO_LOG(VIDEO, "Loaded %u cached shaders from %s", count, filename.c_str()); +} + +template +static void ClearShaderCache(T& cache) +{ + cache.disk_cache.Sync(); + cache.disk_cache.Close(); + cache.shader_map.clear(); +} + +void ShaderCache::LoadShaderCaches() +{ + // Ubershader caches, if present. + LoadShaderCache(m_uber_vs_cache, m_api_type, + "uber-vs", false); + LoadShaderCache(m_uber_ps_cache, m_api_type, + "uber-ps", false); + + // We also share geometry shaders, as there aren't many variants. + if (m_host_config.backend_geometry_shaders) + LoadShaderCache(m_gs_cache, m_api_type, "gs", false); + + // Specialized shaders, gameid-specific. + LoadShaderCache(m_vs_cache, m_api_type, "specialized-vs", + true); + LoadShaderCache(m_ps_cache, m_api_type, "specialized-ps", + true); +} + +void ShaderCache::ClearShaderCaches() +{ + ClearShaderCache(m_vs_cache); + ClearShaderCache(m_gs_cache); + ClearShaderCache(m_ps_cache); + + ClearShaderCache(m_uber_vs_cache); + ClearShaderCache(m_uber_ps_cache); + + SETSTAT(stats.numPixelShadersCreated, 0); + SETSTAT(stats.numPixelShadersAlive, 0); + SETSTAT(stats.numVertexShadersCreated, 0); + SETSTAT(stats.numVertexShadersAlive, 0); +} + +void ShaderCache::LoadPipelineUIDCache() +{ + // We use the async compiler here to speed up startup time. + class CacheReader : public LinearDiskCacheReader + { + public: + CacheReader(ShaderCache* shader_cache_) : shader_cache(shader_cache_) {} + void Read(const GXPipelineDiskCacheUid& key, const u8* data, u32 data_size) + { + GXPipelineUid config = {}; + config.vertex_format = VertexLoaderManager::GetOrCreateMatchingFormat(key.vertex_decl); + config.vs_uid = key.vs_uid; + config.gs_uid = key.gs_uid; + config.ps_uid = key.ps_uid; + config.rasterization_state.hex = key.rasterization_state_bits; + config.depth_state.hex = key.depth_state_bits; + config.blending_state.hex = key.blending_state_bits; + + auto iter = shader_cache->m_gx_pipeline_cache.find(config); + if (iter != shader_cache->m_gx_pipeline_cache.end()) + return; + + auto& entry = shader_cache->m_gx_pipeline_cache[config]; + entry.second = false; + } + + private: + ShaderCache* shader_cache; + }; + + std::string filename = GetDiskShaderCacheFileName(m_api_type, "pipeline-uid", true, false, false); + CacheReader reader(this); + u32 count = m_gx_pipeline_uid_disk_cache.OpenAndRead(filename, reader); + INFO_LOG(VIDEO, "Read %u pipeline UIDs from %s", count, filename.c_str()); + CompileMissingPipelines(); +} + +void ShaderCache::CompileMissingPipelines() +{ + // Queue all uids with a null pipeline for compilation. + for (auto& it : m_gx_pipeline_cache) + { + if (!it.second.second) + QueuePipelineCompile(it.first); + } + for (auto& it : m_gx_uber_pipeline_cache) + { + if (!it.second.second) + QueueUberPipelineCompile(it.first); + } +} + +void ShaderCache::InvalidateCachedPipelines() +{ + // Set the pending flag to false, and destroy the pipeline. + for (auto& it : m_gx_pipeline_cache) + { + it.second.first.reset(); + it.second.second = false; + } + for (auto& it : m_gx_uber_pipeline_cache) + { + it.second.first.reset(); + it.second.second = false; + } +} + +void ShaderCache::ClearPipelineCaches() +{ + m_gx_pipeline_cache.clear(); + m_gx_uber_pipeline_cache.clear(); +} + +std::unique_ptr ShaderCache::CompileVertexShader(const VertexShaderUid& uid) const +{ + ShaderCode source_code = GenerateVertexShaderCode(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr +ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const +{ + ShaderCode source_code = UberShader::GenVertexShader(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr ShaderCache::CompilePixelShader(const PixelShaderUid& uid) const +{ + ShaderCode source_code = GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr +ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const +{ + ShaderCode source_code = UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +const AbstractShader* ShaderCache::InsertVertexShader(const VertexShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_vs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_vs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertVertexUberShader(const UberShader::VertexShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_uber_vs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_uber_vs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertPixelShader(const PixelShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_ps_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_ps_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertPixelUberShader(const UberShader::PixelShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_uber_ps_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_uber_ps_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::CreateGeometryShader(const GeometryShaderUid& uid) +{ + ShaderCode source_code = GenerateGeometryShaderCode(m_api_type, m_host_config, uid.GetUidData()); + std::unique_ptr shader = g_renderer->CreateShaderFromSource( + ShaderStage::Geometry, source_code.GetBuffer().c_str(), source_code.GetBuffer().size()); + + auto& entry = m_gs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_gs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +bool ShaderCache::NeedsGeometryShader(const GeometryShaderUid& uid) const +{ + return m_host_config.backend_geometry_shaders && !uid.GetUidData()->IsPassthrough(); +} + +AbstractPipelineConfig ShaderCache::GetGXPipelineConfig( + const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader, + const AbstractShader* geometry_shader, const AbstractShader* pixel_shader, + const RasterizationState& rasterization_state, const DepthState& depth_state, + const BlendingState& blending_state) +{ + AbstractPipelineConfig config = {}; + config.usage = AbstractPipelineUsage::GX; + config.vertex_format = vertex_format; + config.vertex_shader = vertex_shader; + config.geometry_shader = geometry_shader; + config.pixel_shader = pixel_shader; + config.rasterization_state = rasterization_state; + config.depth_state = depth_state; + config.blending_state = blending_state; + config.framebuffer_state.color_texture_format = AbstractTextureFormat::RGBA8; + config.framebuffer_state.depth_texture_format = AbstractTextureFormat::D32F; + config.framebuffer_state.per_sample_shading = m_host_config.ssaa; + config.framebuffer_state.samples = m_efb_multisamples; + return config; +} + +std::optional ShaderCache::GetGXPipelineConfig(const GXPipelineUid& config) +{ + const AbstractShader* vs; + auto vs_iter = m_vs_cache.shader_map.find(config.vs_uid); + if (vs_iter != m_vs_cache.shader_map.end() && !vs_iter->second.pending) + vs = vs_iter->second.shader.get(); + else + vs = InsertVertexShader(config.vs_uid, CompileVertexShader(config.vs_uid)); + + const AbstractShader* ps; + auto ps_iter = m_ps_cache.shader_map.find(config.ps_uid); + if (ps_iter != m_ps_cache.shader_map.end() && !ps_iter->second.pending) + ps = ps_iter->second.shader.get(); + else + ps = InsertPixelShader(config.ps_uid, CompilePixelShader(config.ps_uid)); + + if (!vs || !ps) + return {}; + + const AbstractShader* gs = nullptr; + if (NeedsGeometryShader(config.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid); + if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending) + gs = gs_iter->second.shader.get(); + else + gs = CreateGeometryShader(config.gs_uid); + if (!gs) + return {}; + } + + return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state, + config.depth_state, config.blending_state); +} + +std::optional +ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineUid& config) +{ + const AbstractShader* vs; + auto vs_iter = m_uber_vs_cache.shader_map.find(config.vs_uid); + if (vs_iter != m_uber_vs_cache.shader_map.end() && !vs_iter->second.pending) + vs = vs_iter->second.shader.get(); + else + vs = InsertVertexUberShader(config.vs_uid, CompileVertexUberShader(config.vs_uid)); + + const AbstractShader* ps; + auto ps_iter = m_uber_ps_cache.shader_map.find(config.ps_uid); + if (ps_iter != m_uber_ps_cache.shader_map.end() && !ps_iter->second.pending) + ps = ps_iter->second.shader.get(); + else + ps = InsertPixelUberShader(config.ps_uid, CompilePixelUberShader(config.ps_uid)); + + if (!vs || !ps) + return {}; + + const AbstractShader* gs = nullptr; + if (NeedsGeometryShader(config.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid); + if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending) + gs = gs_iter->second.shader.get(); + else + gs = CreateGeometryShader(config.gs_uid); + if (!gs) + return {}; + } + + return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state, + config.depth_state, config.blending_state); +} + +const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineUid& config, + std::unique_ptr pipeline) +{ + auto& entry = m_gx_pipeline_cache[config]; + entry.second = false; + if (!entry.first && pipeline) + entry.first = std::move(pipeline); + + return entry.first.get(); +} + +const AbstractPipeline* +ShaderCache::InsertGXUberPipeline(const GXUberPipelineUid& config, + std::unique_ptr pipeline) +{ + auto& entry = m_gx_uber_pipeline_cache[config]; + entry.second = false; + if (!entry.first && pipeline) + entry.first = std::move(pipeline); + + return entry.first.get(); +} + +void ShaderCache::AppendGXPipelineUID(const GXPipelineUid& config) +{ + // Convert to disk format. + GXPipelineDiskCacheUid disk_uid = {}; + disk_uid.vertex_decl = config.vertex_format->GetVertexDeclaration(); + disk_uid.vs_uid = config.vs_uid; + disk_uid.gs_uid = config.gs_uid; + disk_uid.ps_uid = config.ps_uid; + disk_uid.rasterization_state_bits = config.rasterization_state.hex; + disk_uid.depth_state_bits = config.depth_state.hex; + disk_uid.blending_state_bits = config.blending_state.hex; + m_gx_pipeline_uid_disk_cache.Append(disk_uid, nullptr, 0); +} + +void ShaderCache::QueueVertexShaderCompile(const VertexShaderUid& uid) +{ + class VertexShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + VertexShaderWorkItem(ShaderCache* shader_cache_, const VertexShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompileVertexShader(uid); + return true; + } + + void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + VertexShaderUid uid; + }; + + m_vs_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid) +{ + class VertexUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + VertexUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::VertexShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompileVertexUberShader(uid); + return true; + } + + void Retrieve() override { shader_cache->InsertVertexUberShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + UberShader::VertexShaderUid uid; + }; + + m_uber_vs_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid) +{ + class PixelShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PixelShaderWorkItem(ShaderCache* shader_cache_, const PixelShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompilePixelShader(uid); + return true; + } + + void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + PixelShaderUid uid; + }; + + m_ps_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid) +{ + class PixelUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PixelUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::PixelShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompilePixelUberShader(uid); + return true; + } + + void Retrieve() override { shader_cache->InsertPixelUberShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + UberShader::PixelShaderUid uid; + }; + + m_uber_ps_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePipelineCompile(const GXPipelineUid& uid) +{ + class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineUid& uid_, + const AbstractPipelineConfig& config_) + : shader_cache(shader_cache_), uid(uid_), config(config_) + { + } + + bool Compile() override + { + pipeline = g_renderer->CreatePipeline(config); + return true; + } + + void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } + private: + ShaderCache* shader_cache; + std::unique_ptr pipeline; + GXPipelineUid uid; + AbstractPipelineConfig config; + }; + + auto config = GetGXPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + InsertGXPipeline(uid, nullptr); + return; + } + + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid, *config); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); + m_gx_pipeline_cache[uid].second = true; +} + +void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineUid& uid) +{ + // Since the shaders may not be compiled at pipelines request time, we do this in two passes. + // This is necessary because we can't access the caches in the worker thread. + class UberPipelineCompilePass final : public AsyncShaderCompiler::WorkItem + { + public: + UberPipelineCompilePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_, + const AbstractPipelineConfig& config_) + : shader_cache(shader_cache_), uid(uid_), config(config_) + { + } + + bool Compile() override + { + pipeline = g_renderer->CreatePipeline(config); + return true; + } + + void Retrieve() override { shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); } + private: + ShaderCache* shader_cache; + std::unique_ptr pipeline; + GXUberPipelineUid uid; + AbstractPipelineConfig config; + }; + class UberPipelinePreparePass final : public AsyncShaderCompiler::WorkItem + { + public: + UberPipelinePreparePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override { return true; } + void Retrieve() override + { + auto config = shader_cache->GetGXUberPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + shader_cache->InsertGXUberPipeline(uid, nullptr); + return; + } + + auto wi = shader_cache->m_async_shader_compiler->CreateWorkItem( + shader_cache, uid, *config); + shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi)); + } + + private: + ShaderCache* shader_cache; + GXUberPipelineUid uid; + }; + + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); + m_gx_uber_pipeline_cache[uid].second = true; +} + +void ShaderCache::PrecompileUberShaders() +{ + // Geometry shaders are required for the pipelines. + if (m_host_config.backend_geometry_shaders) + { + EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) { + auto iter = m_gs_cache.shader_map.find(guid); + if (iter == m_gs_cache.shader_map.end()) + CreateGeometryShader(guid); + }); + } + + // Queue shader compiling. + UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) { + auto iter = m_uber_vs_cache.shader_map.find(vuid); + if (iter == m_uber_vs_cache.shader_map.end()) + QueueVertexUberShaderCompile(vuid); + }); + UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) { + auto iter = m_uber_ps_cache.shader_map.find(puid); + if (iter == m_uber_ps_cache.shader_map.end()) + QueuePixelUberShaderCompile(puid); + }); + + // Create a dummy vertex format with no attributes. + // All attributes will be enabled in GetUberVertexFormat. + PortableVertexDeclaration dummy_vertex_decl = {}; + dummy_vertex_decl.position.components = 4; + dummy_vertex_decl.position.type = VAR_FLOAT; + dummy_vertex_decl.position.enable = true; + dummy_vertex_decl.stride = sizeof(float) * 4; + NativeVertexFormat* dummy_vertex_format = + VertexLoaderManager::GetUberVertexFormat(dummy_vertex_decl); + auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid, + const GeometryShaderUid& gs_uid, + const UberShader::PixelShaderUid& ps_uid) { + GXUberPipelineUid config; + config.vertex_format = dummy_vertex_format; + config.vs_uid = vs_uid; + config.gs_uid = gs_uid; + config.ps_uid = ps_uid; + config.rasterization_state = RenderState::GetNoCullRasterizationState(); + config.depth_state = RenderState::GetNoDepthTestingDepthStencilState(); + config.blending_state = RenderState::GetNoBlendingBlendState(); + + auto iter = m_gx_uber_pipeline_cache.find(config); + if (iter != m_gx_uber_pipeline_cache.end()) + return; + + auto& entry = m_gx_uber_pipeline_cache[config]; + entry.second = false; + }; + + // Populate the pipeline configs with empty entries, these will be compiled afterwards. + 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; + + QueueDummyPipeline(vuid, guid, puid); + }); + }); + }); +} + +std::string ShaderCache::GetUtilityShaderHeader() const +{ + std::stringstream ss; + + ss << "#define API_D3D " << (m_api_type == APIType::D3D ? 1 : 0) << "\n"; + ss << "#define API_OPENGL " << (m_api_type == APIType::OpenGL ? 1 : 0) << "\n"; + ss << "#define API_VULKAN " << (m_api_type == APIType::Vulkan ? 1 : 0) << "\n"; + + if (m_efb_multisamples > 1) + { + ss << "#define MSAA_ENABLED 1" << std::endl; + ss << "#define MSAA_SAMPLES " << m_efb_multisamples << std::endl; + if (m_host_config.ssaa) + ss << "#define SSAA_ENABLED 1" << std::endl; + } + + ss << "#define EFB_LAYERS " << (m_host_config.stereo ? 2 : 1) << std::endl; + + return ss.str(); +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h new file mode 100644 index 0000000000..8681df19be --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -0,0 +1,162 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/LinearDiskCache.h" + +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/AsyncShaderCompiler.h" +#include "VideoCommon/GXPipelineTypes.h" +#include "VideoCommon/GeometryShaderGen.h" +#include "VideoCommon/NativeVertexFormat.h" +#include "VideoCommon/PixelShaderGen.h" +#include "VideoCommon/RenderState.h" +#include "VideoCommon/UberShaderPixel.h" +#include "VideoCommon/UberShaderVertex.h" +#include "VideoCommon/VertexShaderGen.h" + +class NativeVertexFormat; + +namespace VideoCommon +{ +class ShaderCache final +{ +public: + ShaderCache(); + ~ShaderCache(); + + // Perform at startup, create descriptor layouts, compiles all static shaders. + bool Initialize(); + void Shutdown(); + + // Changes the shader host config. Shaders will be reloaded if there are changes. + void SetHostConfig(const ShaderHostConfig& host_config, u32 efb_multisamples); + + // Reloads/recreates all shaders and pipelines. + void Reload(); + + // Retrieves all pending shaders/pipelines from the async compiler. + void RetrieveAsyncShaders(); + + // Get utility shader header based on current config. + std::string GetUtilityShaderHeader() const; + + // Accesses ShaderGen shader caches + const AbstractPipeline* GetPipelineForUid(const GXPipelineUid& uid); + const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineUid& uid); + + // Accesses ShaderGen shader caches asynchronously. + // The optional will be empty if this pipeline is now background compiling. + std::optional GetPipelineForUidAsync(const GXPipelineUid& uid); + +private: + void WaitForAsyncCompiler(); + void LoadShaderCaches(); + void ClearShaderCaches(); + void LoadPipelineUIDCache(); + void CompileMissingPipelines(); + void InvalidateCachedPipelines(); + void ClearPipelineCaches(); + void PrecompileUberShaders(); + + // GX shader compiler methods + std::unique_ptr CompileVertexShader(const VertexShaderUid& uid) const; + std::unique_ptr + CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const; + std::unique_ptr CompilePixelShader(const PixelShaderUid& uid) const; + std::unique_ptr + CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const; + const AbstractShader* InsertVertexShader(const VertexShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertVertexUberShader(const UberShader::VertexShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertPixelShader(const PixelShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertPixelUberShader(const UberShader::PixelShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* CreateGeometryShader(const GeometryShaderUid& uid); + bool NeedsGeometryShader(const GeometryShaderUid& uid) const; + + // GX pipeline compiler methods + AbstractPipelineConfig + GetGXPipelineConfig(const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader, + const AbstractShader* geometry_shader, const AbstractShader* pixel_shader, + const RasterizationState& rasterization_state, const DepthState& depth_state, + const BlendingState& blending_state); + std::optional GetGXPipelineConfig(const GXPipelineUid& uid); + std::optional GetGXUberPipelineConfig(const GXUberPipelineUid& uid); + const AbstractPipeline* InsertGXPipeline(const GXPipelineUid& config, + std::unique_ptr pipeline); + const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineUid& config, + std::unique_ptr pipeline); + void AppendGXPipelineUID(const GXPipelineUid& config); + + // ASync Compiler Methods + void QueueVertexShaderCompile(const VertexShaderUid& uid); + void QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid); + void QueuePixelShaderCompile(const PixelShaderUid& uid); + void QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid); + void QueuePipelineCompile(const GXPipelineUid& uid); + void QueueUberPipelineCompile(const GXUberPipelineUid& uid); + + // Configuration bits. + APIType m_api_type = APIType::Nothing; + ShaderHostConfig m_host_config = {}; + u32 m_efb_multisamples = 1; + std::unique_ptr m_async_shader_compiler; + + // GX Shader Caches + template + struct ShaderModuleCache + { + struct Shader + { + std::unique_ptr shader; + bool pending; + }; + std::map shader_map; + LinearDiskCache disk_cache; + }; + ShaderModuleCache m_vs_cache; + ShaderModuleCache m_gs_cache; + ShaderModuleCache m_ps_cache; + ShaderModuleCache m_uber_vs_cache; + ShaderModuleCache m_uber_ps_cache; + + // GX Pipeline Caches - .first - pipeline, .second - pending + std::map, bool>> m_gx_pipeline_cache; + std::map, bool>> + m_gx_uber_pipeline_cache; + + // Disk cache of pipeline UIDs + // We can't use the whole UID as a type + struct GXPipelineDiskCacheUid + { + PortableVertexDeclaration vertex_decl; + VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + PixelShaderUid ps_uid; + u32 rasterization_state_bits; + u32 depth_state_bits; + u32 blending_state_bits; + }; + LinearDiskCache m_gx_pipeline_uid_disk_cache; +}; + +} // namespace VideoCommon + +extern std::unique_ptr g_shader_cache; diff --git a/Source/Core/VideoCommon/ShaderGenCommon.cpp b/Source/Core/VideoCommon/ShaderGenCommon.cpp index 8cc7e54f5c..b4cf9e9a48 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.cpp +++ b/Source/Core/VideoCommon/ShaderGenCommon.cpp @@ -37,28 +37,31 @@ ShaderHostConfig ShaderHostConfig::GetCurrent() } std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid, - bool include_host_config) + bool include_host_config, bool include_api) { if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); std::string filename = File::GetUserPath(D_SHADERCACHE_IDX); - switch (api_type) + if (include_api) { - case APIType::D3D: - filename += "D3D"; - break; - case APIType::OpenGL: - filename += "OpenGL"; - break; - case APIType::Vulkan: - filename += "Vulkan"; - break; - default: - break; + switch (api_type) + { + case APIType::D3D: + filename += "D3D"; + break; + case APIType::OpenGL: + filename += "OpenGL"; + break; + case APIType::Vulkan: + filename += "Vulkan"; + break; + default: + break; + } + filename += '-'; } - filename += '-'; filename += type; if (include_gameid) diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h index 5aa391c412..af9b97cf2f 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.h +++ b/Source/Core/VideoCommon/ShaderGenCommon.h @@ -187,7 +187,7 @@ union ShaderHostConfig // Gets the filename of the specified type of cache object (e.g. vertex shader, pipeline). std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid, - bool include_host_config); + bool include_host_config, bool include_api = true); template inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type, diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 44ff88b633..b54b8060b9 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -105,10 +105,8 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count, Flush(); // Have to update the rasterization state for point/line cull modes. - RasterizationState raster_state = {}; - raster_state.Generate(bpmem, new_primitive_type); - g_renderer->SetRasterizationState(raster_state); m_current_primitive_type = new_primitive_type; + SetRasterizationStateChanged(); } // Check for size in buffer, if the buffer gets full, call Flush() @@ -386,6 +384,10 @@ void VertexManagerBase::Flush() if (!m_cull_all) { + // Update the pipeline, or compile one if needed. + UpdatePipelineConfig(); + UpdatePipelineObject(); + // set the rest of the global constants GeometryShaderManager::SetConstants(); PixelShaderManager::SetConstants(); @@ -477,3 +479,111 @@ void VertexManagerBase::CalculateZSlope(NativeVertexFormat* format) m_zslope.f0 = out[2] - (out[0] * m_zslope.dfdx + out[1] * m_zslope.dfdy); m_zslope.dirty = true; } + +void VertexManagerBase::UpdatePipelineConfig() +{ + NativeVertexFormat* vertex_format = VertexLoaderManager::GetCurrentVertexFormat(); + if (vertex_format != m_current_pipeline_config.vertex_format) + { + m_current_pipeline_config.vertex_format = vertex_format; + m_current_uber_pipeline_config.vertex_format = + VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration()); + m_pipeline_config_changed = true; + } + + VertexShaderUid vs_uid = GetVertexShaderUid(); + if (vs_uid != m_current_pipeline_config.vs_uid) + { + m_current_pipeline_config.vs_uid = vs_uid; + m_current_uber_pipeline_config.vs_uid = UberShader::GetVertexShaderUid(); + m_pipeline_config_changed = true; + } + + PixelShaderUid ps_uid = GetPixelShaderUid(); + if (ps_uid != m_current_pipeline_config.ps_uid) + { + m_current_pipeline_config.ps_uid = ps_uid; + m_current_uber_pipeline_config.ps_uid = UberShader::GetPixelShaderUid(); + m_pipeline_config_changed = true; + } + + GeometryShaderUid gs_uid = GetGeometryShaderUid(GetCurrentPrimitiveType()); + if (gs_uid != m_current_pipeline_config.gs_uid) + { + m_current_pipeline_config.gs_uid = gs_uid; + m_current_uber_pipeline_config.gs_uid = gs_uid; + m_pipeline_config_changed = true; + } + + if (m_rasterization_state_changed) + { + m_rasterization_state_changed = false; + + RasterizationState new_rs = {}; + new_rs.Generate(bpmem, m_current_primitive_type); + if (new_rs != m_current_pipeline_config.rasterization_state) + { + m_current_pipeline_config.rasterization_state = new_rs; + m_current_uber_pipeline_config.rasterization_state = new_rs; + m_pipeline_config_changed = true; + } + } + + if (m_depth_state_changed) + { + m_depth_state_changed = false; + + DepthState new_ds = {}; + new_ds.Generate(bpmem); + if (new_ds != m_current_pipeline_config.depth_state) + { + m_current_pipeline_config.depth_state = new_ds; + m_current_uber_pipeline_config.depth_state = new_ds; + m_pipeline_config_changed = true; + } + } + + if (m_blending_state_changed) + { + m_blending_state_changed = false; + + BlendingState new_bs = {}; + new_bs.Generate(bpmem); + if (new_bs != m_current_pipeline_config.blending_state) + { + m_current_pipeline_config.blending_state = new_bs; + m_current_uber_pipeline_config.blending_state = new_bs; + m_pipeline_config_changed = true; + } + } +} + +void VertexManagerBase::UpdatePipelineObject() +{ + if (!m_pipeline_config_changed) + return; + + m_current_pipeline_object = nullptr; + m_pipeline_config_changed = false; + + if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Disabled) + { + // Ubershaders disabled? Block and compile the specialized shader. + m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config); + return; + } + else if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Hybrid) + { + // Can we background compile shaders? If so, get the pipeline asynchronously. + auto res = g_shader_cache->GetPipelineForUidAsync(m_current_pipeline_config); + if (res) + { + // Specialized shaders are ready, prefer these. + m_current_pipeline_object = *res; + return; + } + } + + // Exclusive ubershader mode, or hybrid and shaders are still compiling. + m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); +} diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 22c9b0d2a6..4d07b08411 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -10,6 +10,7 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/ShaderCache.h" class DataReader; class NativeVertexFormat; @@ -58,6 +59,16 @@ public: std::pair ResetFlushAspectRatioCount(); + // State setters, called from register update functions. + void SetRasterizationStateChanged() { m_rasterization_state_changed = true; } + void SetDepthStateChanged() { m_depth_state_changed = true; } + void SetBlendingStateChanged() { m_blending_state_changed = true; } + void InvalidatePipelineObject() + { + m_current_pipeline_object = nullptr; + m_pipeline_config_changed = true; + } + protected: virtual void vDoState(PointerWrap& p) {} virtual void ResetBuffer(u32 stride) = 0; @@ -72,8 +83,15 @@ protected: Slope m_zslope = {}; void CalculateZSlope(NativeVertexFormat* format); - bool m_cull_all = false; + VideoCommon::GXPipelineUid m_current_pipeline_config; + VideoCommon::GXUberPipelineUid m_current_uber_pipeline_config; + const AbstractPipeline* m_current_pipeline_object = nullptr; PrimitiveType m_current_primitive_type = PrimitiveType::Points; + bool m_pipeline_config_changed = true; + bool m_rasterization_state_changed = true; + bool m_depth_state_changed = true; + bool m_blending_state_changed = true; + bool m_cull_all = false; private: bool m_is_flushed = true; @@ -84,6 +102,8 @@ private: virtual void CreateDeviceObjects() {} virtual void DestroyDeviceObjects() {} + void UpdatePipelineConfig(); + void UpdatePipelineObject(); }; extern std::unique_ptr g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 33931bdb17..b84bfb6d66 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -68,6 +68,7 @@ + @@ -119,6 +120,8 @@ + + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index cc7d80508c..f356cc9ef4 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -197,6 +197,9 @@ Base + + Shader Generators + @@ -378,8 +381,14 @@ Base + + Shader Generators + + + Shader Generators + - + \ No newline at end of file diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 2a4811eece..271223ba3d 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -102,9 +102,8 @@ void VideoConfig::Refresh() bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING); iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL); bShaderCache = Config::Get(Config::GFX_SHADER_CACHE); - bBackgroundShaderCompiling = Config::Get(Config::GFX_BACKGROUND_SHADER_COMPILING); - bDisableSpecializedShaders = Config::Get(Config::GFX_DISABLE_SPECIALIZED_SHADERS); - bPrecompileUberShaders = Config::Get(Config::GFX_PRECOMPILE_UBER_SHADERS); + bWaitForShadersBeforeStarting = Config::Get(Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); + iUberShaderMode = static_cast(Config::Get(Config::GFX_UBERSHADER_MODE)); iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS); iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS); @@ -187,6 +186,9 @@ static u32 GetNumAutoShaderCompilerThreads() u32 VideoConfig::GetShaderCompilerThreads() const { + if (!backend_info.bSupportsBackgroundCompiling) + return 0; + if (iShaderCompilerThreads >= 0) return static_cast(iShaderCompilerThreads); else @@ -195,20 +197,15 @@ u32 VideoConfig::GetShaderCompilerThreads() const u32 VideoConfig::GetShaderPrecompilerThreads() const { + // When using background compilation, always keep the same thread count. + if (bWaitForShadersBeforeStarting) + return GetShaderCompilerThreads(); + + if (!backend_info.bSupportsBackgroundCompiling) + return 0; + if (iShaderPrecompilerThreads >= 0) return static_cast(iShaderPrecompilerThreads); else return GetNumAutoShaderCompilerThreads(); } - -bool VideoConfig::CanPrecompileUberShaders() const -{ - // We don't want to precompile ubershaders if they're never going to be used. - return bPrecompileUberShaders && (bBackgroundShaderCompiling || bDisableSpecializedShaders); -} - -bool VideoConfig::CanBackgroundCompileShaders() const -{ - // We require precompiled ubershaders to background compile shaders. - return bBackgroundShaderCompiling && bPrecompileUberShaders; -} diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index fb05408303..c8bded371b 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -42,6 +42,13 @@ enum class StereoMode : int Nvidia3DVision }; +enum class UberShaderMode : int +{ + Disabled, + Hybrid, + Exclusive +}; + struct ProjectionHackConfig final { bool m_enable; @@ -161,25 +168,9 @@ struct VideoConfig final // Currently only supported with Vulkan. int iCommandBufferExecuteInterval; - // The following options determine the ubershader mode: - // No ubershaders: - // - bBackgroundShaderCompiling = false - // - bDisableSpecializedShaders = false - // Hybrid/background compiling: - // - bBackgroundShaderCompiling = true - // - bDisableSpecializedShaders = false - // Ubershaders only: - // - bBackgroundShaderCompiling = false - // - bDisableSpecializedShaders = true - - // Enable background shader compiling, use ubershaders while waiting. - bool bBackgroundShaderCompiling; - - // Use ubershaders only, don't compile specialized shaders. - bool bDisableSpecializedShaders; - - // Precompile ubershader variants at boot/config reload time. - bool bPrecompileUberShaders; + // Shader compilation settings. + bool bWaitForShadersBeforeStarting; + UberShaderMode iUberShaderMode; // Number of shader compiler threads. // 0 disables background compilation. @@ -227,6 +218,7 @@ struct VideoConfig final bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon bool bSupportsBPTCTextures; bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES + bool bSupportsBackgroundCompiling; } backend_info; // Utility @@ -246,10 +238,9 @@ struct VideoConfig final return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding; } bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; } + bool UsingUberShaders() const { return iUberShaderMode != UberShaderMode::Disabled; } u32 GetShaderCompilerThreads() const; u32 GetShaderPrecompilerThreads() const; - bool CanPrecompileUberShaders() const; - bool CanBackgroundCompileShaders() const; }; extern VideoConfig g_Config;