Merge pull request #6399 from stenzek/videocommon-shader-cache

VideoCommon Shader (Pipeline) Cache
This commit is contained in:
Pierre Bourdon 2018-03-10 08:22:28 +01:00 committed by GitHub
commit a4ef133456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 1793 additions and 3557 deletions

View File

@ -11,12 +11,14 @@
std::unique_ptr<cInterfaceBase> GLInterface; std::unique_ptr<cInterfaceBase> GLInterface;
namespace GLUtil
{
void InitInterface() void InitInterface()
{ {
GLInterface = HostGL_CreateGLInterface(); 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 // generate objects
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
@ -100,3 +102,27 @@ GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string&
return programID; 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);
}
}
}
}

View File

@ -8,12 +8,9 @@
#include "Common/GL/GLExtensions/GLExtensions.h" #include "Common/GL/GLExtensions/GLExtensions.h"
#ifndef _WIN32 namespace GLUtil
{
#include <sys/types.h>
#endif
void InitInterface(); void InitInterface();
GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader);
// Helpers void EnablePrimitiveRestart();
GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); }

View File

@ -180,13 +180,16 @@ void DolphinAnalytics::MakeBaseBuilder()
static const char* GetUbershaderMode(const VideoConfig& video_config) static const char* GetUbershaderMode(const VideoConfig& video_config)
{ {
if (video_config.bDisableSpecializedShaders) switch (video_config.iUberShaderMode)
{
case UberShaderMode::Exclusive:
return "exclusive"; return "exclusive";
case UberShaderMode::Hybrid:
if (video_config.bBackgroundShaderCompiling)
return "hybrid"; return "hybrid";
case UberShaderMode::Disabled:
return "disabled"; default:
return "disabled";
}
} }
void DolphinAnalytics::MakePerGameBuilder() void DolphinAnalytics::MakePerGameBuilder()

View File

@ -76,12 +76,10 @@ const ConfigInfo<bool> GFX_BACKEND_MULTITHREADING{
const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{ const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{
{System::GFX, "Settings", "CommandBufferExecuteInterval"}, 100}; {System::GFX, "Settings", "CommandBufferExecuteInterval"}, 100};
const ConfigInfo<bool> GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true}; const ConfigInfo<bool> GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true};
const ConfigInfo<bool> GFX_BACKGROUND_SHADER_COMPILING{ const ConfigInfo<bool> GFX_WAIT_FOR_SHADERS_BEFORE_STARTING{
{System::GFX, "Settings", "BackgroundShaderCompiling"}, false}; {System::GFX, "Settings", "WaitForShadersBeforeStarting"}, false};
const ConfigInfo<bool> GFX_DISABLE_SPECIALIZED_SHADERS{ const ConfigInfo<int> GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"},
{System::GFX, "Settings", "DisableSpecializedShaders"}, false}; static_cast<int>(UberShaderMode::Disabled)};
const ConfigInfo<bool> GFX_PRECOMPILE_UBER_SHADERS{
{System::GFX, "Settings", "PrecompileUberShaders"}, true};
const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS{ const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS{
{System::GFX, "Settings", "ShaderCompilerThreads"}, 1}; {System::GFX, "Settings", "ShaderCompilerThreads"}, 1};
const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS{ const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS{

View File

@ -59,9 +59,8 @@ extern const ConfigInfo<bool> GFX_ENABLE_VALIDATION_LAYER;
extern const ConfigInfo<bool> GFX_BACKEND_MULTITHREADING; extern const ConfigInfo<bool> GFX_BACKEND_MULTITHREADING;
extern const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL; extern const ConfigInfo<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL;
extern const ConfigInfo<bool> GFX_SHADER_CACHE; extern const ConfigInfo<bool> GFX_SHADER_CACHE;
extern const ConfigInfo<bool> GFX_BACKGROUND_SHADER_COMPILING; extern const ConfigInfo<bool> GFX_WAIT_FOR_SHADERS_BEFORE_STARTING;
extern const ConfigInfo<bool> GFX_DISABLE_SPECIALIZED_SHADERS; extern const ConfigInfo<int> GFX_UBERSHADER_MODE;
extern const ConfigInfo<bool> GFX_PRECOMPILE_UBER_SHADERS;
extern const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS; extern const ConfigInfo<int> GFX_SHADER_COMPILER_THREADS;
extern const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS; extern const ConfigInfo<int> GFX_SHADER_PRECOMPILER_THREADS;

View File

@ -46,10 +46,8 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location)
Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location, Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location,
Config::GFX_ENABLE_VALIDATION_LAYER.location, Config::GFX_BACKEND_MULTITHREADING.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_COMMAND_BUFFER_EXECUTE_INTERVAL.location, Config::GFX_SHADER_CACHE.location,
Config::GFX_BACKGROUND_SHADER_COMPILING.location, Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING.location, Config::GFX_UBERSHADER_MODE.location,
Config::GFX_DISABLE_SPECIALIZED_SHADERS.location, Config::GFX_SHADER_COMPILER_THREADS.location, Config::GFX_SHADER_PRECOMPILER_THREADS.location,
Config::GFX_PRECOMPILE_UBER_SHADERS.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_ZCOMPLOC.location, Config::GFX_SW_ZFREEZE.location,
Config::GFX_SW_DUMP_OBJECTS.location, Config::GFX_SW_DUMP_TEV_STAGES.location, Config::GFX_SW_DUMP_OBJECTS.location, Config::GFX_SW_DUMP_TEV_STAGES.location,

View File

@ -63,9 +63,8 @@ void EnhancementsWidget::CreateWidgets()
m_af_combo = new GraphicsChoice({tr("1x"), tr("2x"), tr("4x"), tr("8x"), tr("16x")}, m_af_combo = new GraphicsChoice({tr("1x"), tr("2x"), tr("4x"), tr("8x"), tr("16x")},
Config::GFX_ENHANCE_MAX_ANISOTROPY); Config::GFX_ENHANCE_MAX_ANISOTROPY);
m_ubershader_combo = new QComboBox; m_ubershader_combo = new GraphicsChoice({tr("Disabled"), tr("Hybrid"), tr("Exclusive")},
for (const auto& option : {tr("Disabled"), tr("Hybrid"), tr("Exclusive")}) Config::GFX_UBERSHADER_MODE);
m_ubershader_combo->addItem(option);
m_pp_effect = new QComboBox(); m_pp_effect = new QComboBox();
m_configure_pp_effect = new QPushButton(tr("Configure")); m_configure_pp_effect = new QPushButton(tr("Configure"));
@ -131,9 +130,6 @@ void EnhancementsWidget::ConnectWidgets()
{ {
connect(m_aa_combo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(m_aa_combo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); }); [this](int) { SaveSettings(); });
connect(m_ubershader_combo,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); });
connect(m_pp_effect, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(m_pp_effect, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); }); [this](int) { SaveSettings(); });
connect(m_3d_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(m_3d_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
@ -156,13 +152,6 @@ void EnhancementsWidget::LoadSettings()
QString::fromStdString(std::to_string(aa_selection) + "x " + (ssaa ? "SSAA" : "MSAA"))); QString::fromStdString(std::to_string(aa_selection) + "x " + (ssaa ? "SSAA" : "MSAA")));
m_aa_combo->setEnabled(m_aa_combo->count() > 1); 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 // Post Processing Shader
std::vector<std::string> shaders = std::vector<std::string> shaders =
g_Config.stereo_mode == StereoMode::Anaglyph ? g_Config.stereo_mode == StereoMode::Anaglyph ?
@ -220,10 +209,6 @@ void EnhancementsWidget::SaveSettings()
Config::SetBaseOrCurrent(Config::GFX_SSAA, is_ssaa); 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, Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER,
m_pp_effect->currentText().toStdString()); m_pp_effect->currentText().toStdString());

View File

@ -87,6 +87,8 @@ void GeneralWidget::CreateWidgets()
m_keep_window_top = new QCheckBox(tr("Keep Window on Top")); m_keep_window_top = new QCheckBox(tr("Keep Window on Top"));
m_hide_cursor = new QCheckBox(tr("Hide Mouse Cursor")); m_hide_cursor = new QCheckBox(tr("Hide Mouse Cursor"));
m_render_main_window = new QCheckBox(tr("Render to Main Window")); 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); 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_hide_cursor, 3, 0);
m_options_layout->addWidget(m_render_main_window, 3, 1); 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_video_box);
main_layout->addWidget(m_options_box); main_layout->addWidget(m_options_box);
@ -265,6 +268,12 @@ void GeneralWidget::AddDescriptions()
static const char* TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION = static const char* TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION =
QT_TR_NOOP("When playing on NetPlay, show chat messages, buffer changes and " QT_TR_NOOP("When playing on NetPlay, show chat messages, buffer changes and "
"desync alerts.\n\nIf unsure, leave this unchecked."); "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); AddDescription(m_backend_combo, TR_BACKEND_DESCRIPTION);
#ifdef _WIN32 #ifdef _WIN32
@ -282,6 +291,7 @@ void GeneralWidget::AddDescriptions()
AddDescription(m_show_messages, TR_SHOW_FPS_DESCRIPTION); AddDescription(m_show_messages, TR_SHOW_FPS_DESCRIPTION);
AddDescription(m_keep_window_top, TR_KEEP_WINDOW_ON_TOP_DESCRIPTION); AddDescription(m_keep_window_top, TR_KEEP_WINDOW_ON_TOP_DESCRIPTION);
AddDescription(m_show_messages, TR_SHOW_NETPLAY_MESSAGES_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) void GeneralWidget::OnBackendChanged(const QString& backend_name)
{ {

View File

@ -52,6 +52,7 @@ private:
QCheckBox* m_keep_window_top; QCheckBox* m_keep_window_top;
QCheckBox* m_hide_cursor; QCheckBox* m_hide_cursor;
QCheckBox* m_render_main_window; QCheckBox* m_render_main_window;
QCheckBox* m_wait_for_shaders;
X11Utils::XRRConfiguration* m_xrr_config; X11Utils::XRRConfiguration* m_xrr_config;
}; };

View File

@ -317,6 +317,12 @@ static wxString ubershader_desc =
"stuttering. Balances performance and smoothness.\n\n" "stuttering. Balances performance and smoothness.\n\n"
"Exclusive: Ubershaders will always be used. Only recommended for high-end " "Exclusive: Ubershaders will always be used. Only recommended for high-end "
"systems."); "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) VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title)
: wxDialog(parent, wxID_ANY, wxString::Format(_("Dolphin %s Graphics Configuration"), : 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), wxGetTranslation(backend_multithreading_desc),
Config::GFX_BACKEND_MULTITHREADING)); 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 = wxStaticBoxSizer* const group_basic =
@ -534,24 +544,13 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title)
// ubershaders // ubershaders
{ {
const std::array<wxString, 3> mode_choices = {{_("Disabled"), _("Hybrid"), _("Exclusive")}}; const std::array<wxString, 3> mode_choices = {{_("Disabled"), _("Hybrid"), _("Exclusive")}};
wxChoice* const choice_mode =
new wxChoice(page_enh, wxID_ANY, wxDefaultPosition, wxDefaultSize,
static_cast<int>(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), szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Ubershaders:")), wxGBPosition(row, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL); 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; 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 // postproc shader
@ -1290,13 +1289,3 @@ void VideoConfigDiag::OnAAChanged(wxCommandEvent& ev)
Config::SetBaseOrCurrent(Config::GFX_MSAA, vconfig.backend_info.AAModes[mode]); 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);
}

View File

@ -139,7 +139,6 @@ protected:
void PopulatePostProcessingShaders(); void PopulatePostProcessingShaders();
void PopulateAAList(); void PopulateAAList();
void OnAAChanged(wxCommandEvent& ev); void OnAAChanged(wxCommandEvent& ev);
void OnUberShaderModeChanged(wxCommandEvent& ev);
wxChoice* choice_backend; wxChoice* choice_backend;
wxChoice* choice_adapter; wxChoice* choice_adapter;

View File

@ -212,6 +212,7 @@ StateCache::~StateCache()
ID3D11SamplerState* StateCache::Get(SamplerState state) ID3D11SamplerState* StateCache::Get(SamplerState state)
{ {
std::lock_guard<std::mutex> guard(m_lock);
auto it = m_sampler.find(state.hex); auto it = m_sampler.find(state.hex);
if (it != m_sampler.end()) if (it != m_sampler.end())
return it->second; return it->second;
@ -266,6 +267,7 @@ ID3D11SamplerState* StateCache::Get(SamplerState state)
ID3D11BlendState* StateCache::Get(BlendingState state) ID3D11BlendState* StateCache::Get(BlendingState state)
{ {
std::lock_guard<std::mutex> guard(m_lock);
auto it = m_blend.find(state.hex); auto it = m_blend.find(state.hex);
if (it != m_blend.end()) if (it != m_blend.end())
return it->second; return it->second;
@ -348,6 +350,7 @@ ID3D11BlendState* StateCache::Get(BlendingState state)
ID3D11RasterizerState* StateCache::Get(RasterizationState state) ID3D11RasterizerState* StateCache::Get(RasterizationState state)
{ {
std::lock_guard<std::mutex> guard(m_lock);
auto it = m_raster.find(state.hex); auto it = m_raster.find(state.hex);
if (it != m_raster.end()) if (it != m_raster.end())
return it->second; return it->second;
@ -372,6 +375,7 @@ ID3D11RasterizerState* StateCache::Get(RasterizationState state)
ID3D11DepthStencilState* StateCache::Get(DepthState state) ID3D11DepthStencilState* StateCache::Get(DepthState state)
{ {
std::lock_guard<std::mutex> guard(m_lock);
auto it = m_depth.find(state.hex); auto it = m_depth.find(state.hex);
if (it != m_depth.end()) if (it != m_depth.end())
return it->second; return it->second;

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <mutex>
#include <unordered_map> #include <unordered_map>
#include "Common/BitField.h" #include "Common/BitField.h"
@ -35,6 +36,7 @@ private:
std::unordered_map<u32, ID3D11RasterizerState*> m_raster; std::unordered_map<u32, ID3D11RasterizerState*> m_raster;
std::unordered_map<u32, ID3D11BlendState*> m_blend; std::unordered_map<u32, ID3D11BlendState*> m_blend;
std::unordered_map<SamplerState::StorageType, ID3D11SamplerState*> m_sampler; std::unordered_map<SamplerState::StorageType, ID3D11SamplerState*> m_sampler;
std::mutex m_lock;
}; };
namespace D3D namespace D3D

View File

@ -6,7 +6,6 @@
#include "Common/Align.h" #include "Common/Align.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
@ -25,16 +24,9 @@
namespace DX11 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* ClearGeometryShader = nullptr;
ID3D11GeometryShader* CopyGeometryShader = nullptr; ID3D11GeometryShader* CopyGeometryShader = nullptr;
LinearDiskCache<GeometryShaderUid, u8> g_gs_disk_cache;
ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader() ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader()
{ {
return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? ClearGeometryShader : nullptr; return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? ClearGeometryShader : nullptr;
@ -63,16 +55,6 @@ ID3D11Buffer*& GeometryShaderCache::GetConstantBuffer()
return gscbuf; return gscbuf;
} }
// this class will load the precompiled shaders into our cache
class GeometryShaderCacheInserter : public LinearDiskCacheReader<GeometryShaderUid, u8>
{
public:
void Read(const GeometryShaderUid& key, const u8* value, u32 value_size)
{
GeometryShaderCache::InsertByteCode(key, value, value_size);
}
};
const char clear_shader_code[] = { const char clear_shader_code[] = {
"struct VSOUTPUT\n" "struct VSOUTPUT\n"
"{\n" "{\n"
@ -155,44 +137,6 @@ void GeometryShaderCache::Init()
CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code); CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code);
CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader"); CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader");
D3D::SetDebugObjectName(CopyGeometryShader, "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() void GeometryShaderCache::Shutdown()
@ -201,83 +145,5 @@ void GeometryShaderCache::Shutdown()
SAFE_RELEASE(ClearGeometryShader); SAFE_RELEASE(ClearGeometryShader);
SAFE_RELEASE(CopyGeometryShader); 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 } // DX11

View File

@ -15,36 +15,12 @@ class GeometryShaderCache
{ {
public: public:
static void Init(); static void Init();
static void Reload();
static void Clear();
static void Shutdown(); 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* GetClearGeometryShader();
static ID3D11GeometryShader* GetCopyGeometryShader(); static ID3D11GeometryShader* GetCopyGeometryShader();
static ID3D11Buffer*& GetConstantBuffer(); static ID3D11Buffer*& GetConstantBuffer();
private:
struct GSCacheEntry
{
ID3D11GeometryShader* shader;
GSCacheEntry() : shader(nullptr) {}
void Destroy() { SAFE_RELEASE(shader); }
};
typedef std::map<GeometryShaderUid, GSCacheEntry> GSCache;
static void LoadShaderCache();
static GSCache GeometryShaders;
static const GSCacheEntry* last_entry;
static GeometryShaderUid last_uid;
static const GSCacheEntry pass_entry;
}; };
} // namespace DX11 } // namespace DX11

View File

@ -13,6 +13,8 @@
namespace DX11 namespace DX11
{ {
std::mutex s_input_layout_lock;
std::unique_ptr<NativeVertexFormat> std::unique_ptr<NativeVertexFormat>
VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
{ {
@ -116,23 +118,34 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl)
D3DVertexFormat::~D3DVertexFormat() D3DVertexFormat::~D3DVertexFormat()
{ {
SAFE_RELEASE(m_layout); ID3D11InputLayout* layout = m_layout.load();
SAFE_RELEASE(layout);
} }
ID3D11InputLayout* D3DVertexFormat::GetInputLayout(D3DBlob* vs_bytecode) ID3D11InputLayout* D3DVertexFormat::GetInputLayout(D3DBlob* vs_bytecode)
{ {
if (m_layout) // CreateInputLayout requires a shader input, but it only looks at the signature of the shader,
return m_layout; // 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( 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)) if (FAILED(hr))
PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__);
DX11::D3D::SetDebugObjectName(m_layout, "input layout used to emulate the GX pipeline"); 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 } // namespace DX11

View File

@ -7,7 +7,6 @@
#include "Common/Align.h" #include "Common/Align.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -27,17 +26,6 @@
namespace DX11 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<PixelShaderUid, u8> g_ps_disk_cache;
LinearDiskCache<UberShader::PixelShaderUid, u8> g_uber_ps_disk_cache;
extern std::unique_ptr<VideoCommon::AsyncShaderCompiler> g_async_compiler;
ID3D11PixelShader* s_ColorCopyProgram[2] = {nullptr}; ID3D11PixelShader* s_ColorCopyProgram[2] = {nullptr};
ID3D11PixelShader* s_ClearProgram = nullptr; ID3D11PixelShader* s_ClearProgram = nullptr;
ID3D11PixelShader* s_AnaglyphProgram = nullptr; ID3D11PixelShader* s_AnaglyphProgram = nullptr;
@ -309,17 +297,6 @@ ID3D11Buffer* PixelShaderCache::GetConstantBuffer()
return pscbuf; return pscbuf;
} }
// this class will load the precompiled shaders into our cache
template <typename UidType>
class PixelShaderCacheInserter : public LinearDiskCacheReader<UidType, u8>
{
public:
void Read(const UidType& key, const u8* value, u32 value_size)
{
PixelShaderCache::InsertByteCode(key, value, value_size);
}
};
void PixelShaderCache::Init() void PixelShaderCache::Init()
{ {
unsigned int cbsize = Common::AlignUp(static_cast<unsigned int>(sizeof(PixelShaderConstants)), unsigned int cbsize = Common::AlignUp(static_cast<unsigned int>(sizeof(PixelShaderConstants)),
@ -344,58 +321,6 @@ void PixelShaderCache::Init()
s_ColorCopyProgram[0] = D3D::CompileAndCreatePixelShader(color_copy_program_code); s_ColorCopyProgram[0] = D3D::CompileAndCreatePixelShader(color_copy_program_code);
CHECK(s_ColorCopyProgram[0] != nullptr, "Create color copy pixel shader"); CHECK(s_ColorCopyProgram[0] != nullptr, "Create color copy pixel shader");
D3D::SetDebugObjectName(s_ColorCopyProgram[0], "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<PixelShaderUid> inserter;
g_ps_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "PS", true, true), inserter);
PixelShaderCacheInserter<UberShader::PixelShaderUid> 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 // 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_rgba6_to_rgb8[i]);
SAFE_RELEASE(s_rgb8_to_rgba6[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<PixelShaderCompilerWorkItem>(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<UberPixelShaderCompilerWorkItem>(uid));
});
g_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
static_cast<int>(completed), static_cast<int>(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 } // DX11

View File

@ -19,16 +19,7 @@ class PixelShaderCache
{ {
public: public:
static void Init(); static void Init();
static void Reload();
static void Clear();
static void Shutdown(); 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(); static ID3D11Buffer* GetConstantBuffer();
@ -40,58 +31,6 @@ public:
static ID3D11PixelShader* ReinterpRGB8ToRGBA6(bool multisampled); static ID3D11PixelShader* ReinterpRGB8ToRGBA6(bool multisampled);
static void InvalidateMSAAShaders(); 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<PixelShaderUid, PSCacheEntry> PSCache;
typedef std::map<UberShader::PixelShaderUid, PSCacheEntry> 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 } // namespace DX11

View File

@ -71,15 +71,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height); g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
SetupDeviceObjects(); 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 // Clear EFB textures
constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}}; constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), 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) void Renderer::SetPipeline(const AbstractPipeline* pipeline)
{ {
const DXPipeline* dx_pipeline = static_cast<const DXPipeline*>(pipeline); const DXPipeline* dx_pipeline = static_cast<const DXPipeline*>(pipeline);
if (!dx_pipeline)
return;
D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState()); D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState());
D3D::stateman->SetDepthState(dx_pipeline->GetDepthState()); 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, void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
u32 vertex_stride, u32 num_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. // Copy in uniforms.
if (uniforms_size > 0) if (uniforms_size > 0)
{ {
@ -638,11 +626,6 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
RestoreAPIState(); 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. // This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks, void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
float Gamma) float Gamma)
@ -683,7 +666,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
// Enable configuration changes // Enable configuration changes
UpdateActiveConfig(); UpdateActiveConfig();
g_texture_cache->OnConfigChanged(g_ActiveConfig); g_texture_cache->OnConfigChanged(g_ActiveConfig);
VertexShaderCache::RetreiveAsyncShaders();
// Flip/present backbuffer to frontbuffer here // Flip/present backbuffer to frontbuffer here
if (D3D::swapchain) if (D3D::swapchain)
@ -706,12 +688,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
D3D11_CLEAR_DEPTH, 0.f, 0); D3D11_CLEAR_DEPTH, 0.f, 0);
} }
if (CheckForHostConfigChanges()) CheckForHostConfigChanges();
{
VertexShaderCache::Reload();
GeometryShaderCache::Reload();
PixelShaderCache::Reload();
}
// begin next frame // begin next frame
RestoreAPIState(); RestoreAPIState();
@ -784,30 +761,6 @@ void Renderer::RestoreAPIState()
BPFunctions::SetScissor(); 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<u32>(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) void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer)
{ {
const DXFramebuffer* fb = static_cast<const DXFramebuffer*>(framebuffer); const DXFramebuffer* fb = static_cast<const DXFramebuffer*>(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) void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
{ {
D3D::stateman->SetTexture( D3D::stateman->SetTexture(
@ -857,7 +800,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
void Renderer::SetSamplerState(u32 index, const SamplerState& state) 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) void Renderer::UnbindTexture(const AbstractTexture* texture)

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <array>
#include <d3d11.h> #include <d3d11.h>
#include <string> #include <string>
#include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DState.h"
@ -41,10 +40,7 @@ public:
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
const ClearColor& color_value = {}, const ClearColor& color_value = {},
float depth_value = 0.0f) override; float depth_value = 0.0f) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override; void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
void SetDepthState(const DepthState& state) override;
void SetTexture(u32 index, const AbstractTexture* texture) override; void SetTexture(u32 index, const AbstractTexture* texture) override;
void SetSamplerState(u32 index, const SamplerState& state) override; void SetSamplerState(u32 index, const SamplerState& state) override;
void UnbindTexture(const AbstractTexture* texture) override; void UnbindTexture(const AbstractTexture* texture) override;
@ -54,10 +50,6 @@ public:
void SetFullscreen(bool enable_fullscreen) override; void SetFullscreen(bool enable_fullscreen) override;
bool IsFullscreen() const 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; 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; 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; u32 groups_x, u32 groups_y, u32 groups_z) override;
private: private:
struct GXPipelineState
{
std::array<SamplerState, 8> samplers;
BlendingState blend;
DepthState zmode;
RasterizationState raster;
};
void SetupDeviceObjects(); void SetupDeviceObjects();
void TeardownDeviceObjects(); void TeardownDeviceObjects();
void Create3DVisionTexture(int width, int height); void Create3DVisionTexture(int width, int height);
@ -106,7 +90,6 @@ private:
void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices); void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices);
StateCache m_state_cache; StateCache m_state_cache;
GXPipelineState m_gx_state;
std::array<ID3D11BlendState*, 4> m_clear_blend_states{}; std::array<ID3D11BlendState*, 4> m_clear_blend_states{};
std::array<ID3D11DepthStencilState*, 3> m_clear_depth_states{}; std::array<ID3D11DepthStencilState*, 3> m_clear_depth_states{};

View File

@ -11,6 +11,7 @@
#include "VideoBackends/D3D/BoundingBox.h" #include "VideoBackends/D3D/BoundingBox.h"
#include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/GeometryShaderCache.h" #include "VideoBackends/D3D/GeometryShaderCache.h"
#include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoBackends/D3D/PixelShaderCache.h"
#include "VideoBackends/D3D/Render.h" #include "VideoBackends/D3D/Render.h"
@ -135,42 +136,23 @@ void VertexManager::Draw(u32 stride)
void VertexManager::vFlush() 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<D3DVertexFormat*>(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(); u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride();
PrepareDrawBuffers(stride); 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); Draw(stride);
g_renderer->RestoreState();
} }
void VertexManager::ResetBuffer(u32 stride) void VertexManager::ResetBuffer(u32 stride)

View File

@ -7,6 +7,7 @@
#include <d3d11.h> #include <d3d11.h>
#include <array> #include <array>
#include <atomic>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -29,7 +30,7 @@ private:
std::array<D3D11_INPUT_ELEMENT_DESC, 32> m_elems{}; std::array<D3D11_INPUT_ELEMENT_DESC, 32> m_elems{};
UINT m_num_elems = 0; UINT m_num_elems = 0;
ID3D11InputLayout* m_layout = nullptr; std::atomic<ID3D11InputLayout*> m_layout{nullptr};
}; };
class VertexManager : public VertexManagerBase class VertexManager : public VertexManagerBase

View File

@ -7,7 +7,6 @@
#include "Common/Align.h" #include "Common/Align.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -28,22 +27,11 @@
namespace DX11 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* SimpleVertexShader = nullptr;
static ID3D11VertexShader* ClearVertexShader = nullptr; static ID3D11VertexShader* ClearVertexShader = nullptr;
static ID3D11InputLayout* SimpleLayout = nullptr; static ID3D11InputLayout* SimpleLayout = nullptr;
static ID3D11InputLayout* ClearLayout = nullptr; static ID3D11InputLayout* ClearLayout = nullptr;
LinearDiskCache<VertexShaderUid, u8> g_vs_disk_cache;
LinearDiskCache<UberShader::VertexShaderUid, u8> g_uber_vs_disk_cache;
std::unique_ptr<VideoCommon::AsyncShaderCompiler> g_async_compiler;
ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader()
{ {
return SimpleVertexShader; return SimpleVertexShader;
@ -164,73 +152,12 @@ void VertexShaderCache::Init()
D3D::SetDebugObjectName(ClearVertexShader, "clear vertex shader"); D3D::SetDebugObjectName(ClearVertexShader, "clear vertex shader");
D3D::SetDebugObjectName(ClearLayout, "clear input layout"); D3D::SetDebugObjectName(ClearLayout, "clear input layout");
Clear();
SETSTAT(stats.numVertexShadersCreated, 0); SETSTAT(stats.numVertexShadersCreated, 0);
SETSTAT(stats.numVertexShadersAlive, 0); SETSTAT(stats.numVertexShadersAlive, 0);
if (g_ActiveConfig.bShaderCache)
LoadShaderCache();
g_async_compiler = std::make_unique<VideoCommon::AsyncShaderCompiler>();
g_async_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ?
g_ActiveConfig.GetShaderPrecompilerThreads() :
g_ActiveConfig.GetShaderCompilerThreads());
if (g_ActiveConfig.CanPrecompileUberShaders())
QueueUberShaderCompiles();
}
void VertexShaderCache::LoadShaderCache()
{
VertexShaderCacheInserter<VertexShaderUid> inserter;
g_vs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "VS", true, true), inserter);
VertexShaderCacheInserter<UberShader::VertexShaderUid> 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() void VertexShaderCache::Shutdown()
{ {
g_async_compiler->StopWorkerThreads();
g_async_compiler->RetrieveWorkItems();
SAFE_RELEASE(vscbuf); SAFE_RELEASE(vscbuf);
SAFE_RELEASE(SimpleVertexShader); SAFE_RELEASE(SimpleVertexShader);
@ -238,271 +165,5 @@ void VertexShaderCache::Shutdown()
SAFE_RELEASE(SimpleLayout); SAFE_RELEASE(SimpleLayout);
SAFE_RELEASE(ClearLayout); 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<VertexShaderCompilerWorkItem>(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<D3DVertexFormat*>(
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<int>(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<UberVertexShaderCompilerWorkItem>(uid));
});
}
void VertexShaderCache::WaitForBackgroundCompilesToComplete()
{
g_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
static_cast<int>(completed), static_cast<int>(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 } // namespace DX11

View File

@ -21,14 +21,7 @@ class VertexShaderCache
{ {
public: public:
static void Init(); static void Init();
static void Reload();
static void Clear();
static void Shutdown(); 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(); static ID3D11Buffer*& GetConstantBuffer();
@ -36,76 +29,6 @@ public:
static ID3D11VertexShader* GetClearVertexShader(); static ID3D11VertexShader* GetClearVertexShader();
static ID3D11InputLayout* GetSimpleInputLayout(); static ID3D11InputLayout* GetSimpleInputLayout();
static ID3D11InputLayout* GetClearInputLayout(); 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<VertexShaderUid, VSCacheEntry> VSCache;
typedef std::map<UberShader::VertexShaderUid, VSCacheEntry> 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 } // namespace DX11

View File

@ -21,6 +21,7 @@
#include "VideoBackends/D3D/VertexShaderCache.h" #include "VideoBackends/D3D/VertexShaderCache.h"
#include "VideoBackends/D3D/VideoBackend.h" #include "VideoBackends/D3D/VideoBackend.h"
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
@ -69,6 +70,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsDynamicSamplerIndexing = false; g_Config.backend_info.bSupportsDynamicSamplerIndexing = false;
g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false;
g_Config.backend_info.bSupportsFramebufferFetch = false; g_Config.backend_info.bSupportsFramebufferFetch = false;
g_Config.backend_info.bSupportsBackgroundCompiling = true;
IDXGIFactory2* factory; IDXGIFactory2* factory;
IDXGIAdapter* ad; IDXGIAdapter* ad;
@ -148,6 +150,7 @@ bool VideoBackend::Initialize(void* window_handle)
// internal interfaces // internal interfaces
g_renderer = std::make_unique<Renderer>(backbuffer_width, backbuffer_height); g_renderer = std::make_unique<Renderer>(backbuffer_width, backbuffer_height);
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
g_vertex_manager = std::make_unique<VertexManager>(); g_vertex_manager = std::make_unique<VertexManager>();
g_perf_query = std::make_unique<PerfQuery>(); g_perf_query = std::make_unique<PerfQuery>();
@ -155,7 +158,9 @@ bool VideoBackend::Initialize(void* window_handle)
VertexShaderCache::Init(); VertexShaderCache::Init();
PixelShaderCache::Init(); PixelShaderCache::Init();
GeometryShaderCache::Init(); GeometryShaderCache::Init();
VertexShaderCache::WaitForBackgroundCompilesToComplete(); if (!g_shader_cache->Initialize())
return false;
D3D::InitUtils(); D3D::InitUtils();
BBox::Init(); BBox::Init();
return true; return true;
@ -163,6 +168,7 @@ bool VideoBackend::Initialize(void* window_handle)
void VideoBackend::Shutdown() void VideoBackend::Shutdown()
{ {
g_shader_cache->Shutdown();
g_renderer->Shutdown(); g_renderer->Shutdown();
D3D::ShutdownUtils(); D3D::ShutdownUtils();
@ -174,6 +180,7 @@ void VideoBackend::Shutdown()
g_perf_query.reset(); g_perf_query.reset();
g_vertex_manager.reset(); g_vertex_manager.reset();
g_texture_cache.reset(); g_texture_cache.reset();
g_shader_cache.reset();
g_renderer.reset(); g_renderer.reset();
ShutdownShared(); ShutdownShared();

View File

@ -3,7 +3,6 @@ set(SRCS
NullTexture.cpp NullTexture.cpp
Render.cpp Render.cpp
VertexManager.cpp VertexManager.cpp
ShaderCache.cpp
) )
set(LIBS set(LIBS

View File

@ -39,14 +39,12 @@
<ClCompile Include="NullBackend.cpp" /> <ClCompile Include="NullBackend.cpp" />
<ClCompile Include="NullTexture.cpp" /> <ClCompile Include="NullTexture.cpp" />
<ClCompile Include="Render.cpp" /> <ClCompile Include="Render.cpp" />
<ClCompile Include="ShaderCache.cpp" />
<ClCompile Include="VertexManager.cpp" /> <ClCompile Include="VertexManager.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="NullTexture.h" /> <ClInclude Include="NullTexture.h" />
<ClInclude Include="PerfQuery.h" /> <ClInclude Include="PerfQuery.h" />
<ClInclude Include="Render.h" /> <ClInclude Include="Render.h" />
<ClInclude Include="ShaderCache.h" />
<ClInclude Include="TextureCache.h" /> <ClInclude Include="TextureCache.h" />
<ClInclude Include="VertexManager.h" /> <ClInclude Include="VertexManager.h" />
<ClInclude Include="VideoBackend.h" /> <ClInclude Include="VideoBackend.h" />

View File

@ -9,7 +9,6 @@
#include "VideoBackends/Null/PerfQuery.h" #include "VideoBackends/Null/PerfQuery.h"
#include "VideoBackends/Null/Render.h" #include "VideoBackends/Null/Render.h"
#include "VideoBackends/Null/ShaderCache.h"
#include "VideoBackends/Null/TextureCache.h" #include "VideoBackends/Null/TextureCache.h"
#include "VideoBackends/Null/VertexManager.h" #include "VideoBackends/Null/VertexManager.h"
#include "VideoBackends/Null/VideoBackend.h" #include "VideoBackends/Null/VideoBackend.h"
@ -47,6 +46,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsST3CTextures = false; g_Config.backend_info.bSupportsST3CTextures = false;
g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false;
g_Config.backend_info.bSupportsFramebufferFetch = false; g_Config.backend_info.bSupportsFramebufferFetch = false;
g_Config.backend_info.bSupportsBackgroundCompiling = false;
// aamodes: We only support 1 sample, so no MSAA // aamodes: We only support 1 sample, so no MSAA
g_Config.backend_info.Adapters.clear(); g_Config.backend_info.Adapters.clear();
@ -63,21 +63,15 @@ bool VideoBackend::Initialize(void* window_handle)
g_perf_query = std::make_unique<PerfQuery>(); g_perf_query = std::make_unique<PerfQuery>();
g_framebuffer_manager = std::make_unique<FramebufferManagerBase>(); g_framebuffer_manager = std::make_unique<FramebufferManagerBase>();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
VertexShaderCache::s_instance = std::make_unique<VertexShaderCache>(); return g_shader_cache->Initialize();
GeometryShaderCache::s_instance = std::make_unique<GeometryShaderCache>();
PixelShaderCache::s_instance = std::make_unique<PixelShaderCache>();
return true;
} }
void VideoBackend::Shutdown() void VideoBackend::Shutdown()
{ {
g_shader_cache->Shutdown();
g_renderer->Shutdown(); g_renderer->Shutdown();
PixelShaderCache::s_instance.reset();
VertexShaderCache::s_instance.reset();
GeometryShaderCache::s_instance.reset();
g_texture_cache.reset(); g_texture_cache.reset();
g_perf_query.reset(); g_perf_query.reset();
g_vertex_manager.reset(); g_vertex_manager.reset();

View File

@ -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 <typename Uid>
ShaderCache<Uid>::ShaderCache()
{
Clear();
SETSTAT(stats.numPixelShadersCreated, 0);
SETSTAT(stats.numPixelShadersAlive, 0);
}
template <typename Uid>
ShaderCache<Uid>::~ShaderCache()
{
Clear();
}
template <typename Uid>
void ShaderCache<Uid>::Clear()
{
m_shaders.clear();
m_last_entry = nullptr;
}
template <typename Uid>
bool ShaderCache<Uid>::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<VertexShaderUid>;
template class ShaderCache<GeometryShaderUid>;
template class ShaderCache<PixelShaderUid>;
std::unique_ptr<VertexShaderCache> VertexShaderCache::s_instance;
std::unique_ptr<GeometryShaderCache> GeometryShaderCache::s_instance;
std::unique_ptr<PixelShaderCache> PixelShaderCache::s_instance;
}

View File

@ -1,85 +0,0 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/VertexShaderGen.h"
#include "VideoCommon/VideoCommon.h"
namespace Null
{
template <typename Uid>
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<Uid, std::string> m_shaders;
const std::string* m_last_entry = nullptr;
Uid m_last_uid;
};
class VertexShaderCache : public ShaderCache<VertexShaderUid>
{
public:
static std::unique_ptr<VertexShaderCache> 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<GeometryShaderUid>
{
public:
static std::unique_ptr<GeometryShaderCache> 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<PixelShaderUid>
{
public:
static std::unique_ptr<PixelShaderCache> 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

View File

@ -4,8 +4,6 @@
#include "VideoBackends/Null/VertexManager.h" #include "VideoBackends/Null/VertexManager.h"
#include "VideoBackends/Null/ShaderCache.h"
#include "VideoCommon/IndexGenerator.h" #include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexLoaderManager.h"
@ -41,9 +39,6 @@ void VertexManager::ResetBuffer(u32 stride)
void VertexManager::vFlush() 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 } // namespace

View File

@ -46,10 +46,11 @@ OGLPipeline::~OGLPipeline()
std::unique_ptr<OGLPipeline> OGLPipeline::Create(const AbstractPipelineConfig& config) std::unique_ptr<OGLPipeline> OGLPipeline::Create(const AbstractPipelineConfig& config)
{ {
const PipelineProgram* program = const PipelineProgram* program = ProgramShaderCache::GetPipelineProgram(
ProgramShaderCache::GetPipelineProgram(static_cast<const OGLShader*>(config.vertex_shader), static_cast<const GLVertexFormat*>(config.vertex_format),
static_cast<const OGLShader*>(config.geometry_shader), static_cast<const OGLShader*>(config.vertex_shader),
static_cast<const OGLShader*>(config.pixel_shader)); static_cast<const OGLShader*>(config.geometry_shader),
static_cast<const OGLShader*>(config.pixel_shader));
if (!program) if (!program)
return nullptr; return nullptr;

View File

@ -33,8 +33,6 @@
#include "VideoCommon/ImageWrite.h" #include "VideoCommon/ImageWrite.h"
#include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/Statistics.h" #include "VideoCommon/Statistics.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexLoaderManager.h"
#include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoCommon.h"
@ -43,8 +41,6 @@ namespace OGL
{ {
static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024; static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024;
std::unique_ptr<ProgramShaderCache::SharedContextAsyncShaderCompiler>
ProgramShaderCache::s_async_compiler;
u32 ProgramShaderCache::s_ubo_buffer_size; u32 ProgramShaderCache::s_ubo_buffer_size;
s32 ProgramShaderCache::s_ubo_align; s32 ProgramShaderCache::s_ubo_align;
GLuint ProgramShaderCache::s_attributeless_VBO = 0; GLuint ProgramShaderCache::s_attributeless_VBO = 0;
@ -54,17 +50,11 @@ GLuint ProgramShaderCache::s_last_VAO = 0;
static std::unique_ptr<StreamBuffer> s_buffer; static std::unique_ptr<StreamBuffer> s_buffer;
static int num_failures = 0; static int num_failures = 0;
static LinearDiskCache<SHADERUID, u8> s_program_disk_cache;
static LinearDiskCache<UBERSHADERUID, u8> s_uber_program_disk_cache;
static GLuint CurrentProgram = 0; static GLuint CurrentProgram = 0;
ProgramShaderCache::PCache ProgramShaderCache::pshaders; ProgramShaderCache::PipelineProgramMap ProgramShaderCache::s_pipeline_programs;
ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders; std::mutex ProgramShaderCache::s_pipeline_program_lock;
ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms;
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_uber_entry;
SHADERUID ProgramShaderCache::last_uid;
UBERSHADERUID ProgramShaderCache::last_uber_uid;
static std::string s_glsl_header = ""; static std::string s_glsl_header = "";
static thread_local bool s_is_shared_context = false;
static std::string GetGLSLVersionString() static std::string GetGLSLVersionString()
{ {
@ -97,37 +87,40 @@ static std::string GetGLSLVersionString()
void SHADER::SetProgramVariables() 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 // 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 std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a);
Bind();
GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock"); // Still need to get sampler locations since we aren't binding them statically in the shaders
GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock"); int loc = glGetUniformLocation(glprogid, name.c_str());
GLint GSBlock_id = glGetUniformBlockIndex(glprogid, "GSBlock"); if (loc != -1)
GLint UBERBlock_id = glGetUniformBlockIndex(glprogid, "UBERBlock"); glUniform1i(loc, a);
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);
}
} }
// Restore previous program binding.
glUseProgram(CurrentProgram);
} }
void SHADER::SetProgramBindings(bool is_compute) 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<ShaderCompileWorkItem>(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<const GLVertexFormat*>(
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, bool ProgramShaderCache::CompileShader(SHADER& shader, const std::string& vcode,
const std::string& pcode, const std::string& gcode) const std::string& pcode, const std::string& gcode)
{ {
@ -616,11 +472,6 @@ bool ProgramShaderCache::CheckProgramLinkResult(GLuint id, const std::string& vc
return true; return true;
} }
ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram()
{
return *last_entry;
}
void ProgramShaderCache::Init() void ProgramShaderCache::Init()
{ {
// We have to get the UBO alignment here because // We have to get the UBO alignment here because
@ -638,93 +489,14 @@ void ProgramShaderCache::Init()
// Then once more to get bytes // Then once more to get bytes
s_buffer = StreamBuffer::Create(GL_UNIFORM_BUFFER, UBO_LENGTH); 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<SharedContextAsyncShaderCompiler>();
// Read our shader cache, only if supported and enabled
if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache)
LoadProgramBinaries();
CreateHeader(); CreateHeader();
CreateAttributelessVAO(); CreateAttributelessVAO();
CurrentProgram = 0; 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() 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(); s_buffer.reset();
glBindVertexArray(0); glBindVertexArray(0);
@ -735,8 +507,8 @@ void ProgramShaderCache::Shutdown()
s_last_VAO = 0; s_last_VAO = 0;
// All pipeline programs should have been released. // All pipeline programs should have been released.
_dbg_assert_(VIDEO, pipelineprograms.empty()); _dbg_assert_(VIDEO, s_pipeline_programs.empty());
pipelineprograms.clear(); s_pipeline_programs.clear();
} }
void ProgramShaderCache::CreateAttributelessVAO() void ProgramShaderCache::CreateAttributelessVAO()
@ -777,146 +549,28 @@ void ProgramShaderCache::InvalidateLastProgram()
CurrentProgram = 0; CurrentProgram = 0;
} }
GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size) const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* vertex_format,
{ const OGLShader* vertex_shader,
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<SHADERUID> inserter(pshaders);
s_program_disk_cache.OpenAndRead(cache_filename, inserter);
// Load global ubershaders.
cache_filename =
GetDiskShaderCacheFileName(APIType::OpenGL, "UberProgramBinaries", false, true);
ProgramShaderCacheInserter<UBERSHADERUID> 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<u8>& 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 <typename CacheMapType, typename DiskCacheType>
static void SaveProgramBinaryMap(CacheMapType& program_map, DiskCacheType& disk_cache)
{
std::vector<u8> 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<u32>(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 OGLShader* geometry_shader, const OGLShader* geometry_shader,
const OGLShader* pixel_shader) const OGLShader* pixel_shader)
{ {
PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader}; PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader};
auto iter = pipelineprograms.find(key);
if (iter != pipelineprograms.end())
{ {
iter->second->reference_count++; std::lock_guard<std::mutex> guard(s_pipeline_program_lock);
return iter->second.get(); 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<PipelineProgram> prog = std::make_unique<PipelineProgram>(); std::unique_ptr<PipelineProgram> prog = std::make_unique<PipelineProgram>();
prog->key = key; prog->key = key;
@ -935,25 +589,50 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v
// Link program. // Link program.
prog->shader.SetProgramBindings(false); prog->shader.SetProgramBindings(false);
glLinkProgram(prog->shader.glprogid); 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, {}, {}, {})) if (!ProgramShaderCache::CheckProgramLinkResult(prog->shader.glprogid, {}, {}, {}))
{ {
prog->shader.Destroy(); prog->shader.Destroy();
return nullptr; 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<std::mutex> 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(); return ip.first->second.get();
} }
void ProgramShaderCache::ReleasePipelineProgram(const PipelineProgram* prog) void ProgramShaderCache::ReleasePipelineProgram(const PipelineProgram* prog)
{ {
auto iter = pipelineprograms.find(prog->key); auto iter = s_pipeline_programs.find(prog->key);
_assert_(iter != pipelineprograms.end() && prog == iter->second.get()); _assert_(iter != s_pipeline_programs.end() && prog == iter->second.get());
if (--iter->second->reference_count == 0) if (--iter->second->reference_count == 0)
{ {
iter->second->shader.Destroy(); iter->second->shader.Destroy();
pipelineprograms.erase(iter); s_pipeline_programs.erase(iter);
} }
} }
@ -1124,345 +803,36 @@ void ProgramShaderCache::CreateHeader()
v >= GlslEs310 ? "precision highp image2DArray;" : ""); v >= GlslEs310 ? "precision highp image2DArray;" : "");
} }
void ProgramShaderCache::PrecompileUberShaders() bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param)
{ {
bool success = true; std::unique_ptr<cInterfaceBase> context = GLInterface->CreateSharedContext();
if (!context)
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<UberShaderCompileWorkItem>(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<int>(completed), static_cast<int>(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)
{ {
PanicAlert("Failed to create shared context for shader compiling."); PanicAlert("Failed to create shared context for shader compiling.");
delete ctx_data;
return false; return false;
} }
*param = ctx_data; *param = context.release();
return true; return true;
} }
bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) bool SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param)
{ {
SharedContextData* ctx_data = reinterpret_cast<SharedContextData*>(param); cInterfaceBase* context = static_cast<cInterfaceBase*>(param);
if (!ctx_data->context->MakeCurrent()) if (!context->MakeCurrent())
{
PanicAlert("Failed to make shared context current.");
ctx_data->context->Shutdown();
delete ctx_data;
return false; return false;
}
CreatePrerenderArrays(ctx_data); s_is_shared_context = true;
return true;
}
void ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param)
{
SharedContextData* ctx_data = reinterpret_cast<SharedContextData*>(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<PrimitiveType>(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<PrimitiveType>(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?
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
{ GLUtil::EnablePrimitiveRestart();
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
{ return true;
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);
}
}
}
} }
void ProgramShaderCache::DestroyPrerenderArrays(SharedContextData* data) void SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param)
{ {
if (data->prerender_VAO) cInterfaceBase* context = static_cast<cInterfaceBase*>(param);
{ context->ClearCurrent();
glDeleteVertexArrays(1, &data->prerender_VAO); delete context;
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);
} }
} // namespace OGL } // namespace OGL

View File

@ -6,20 +6,12 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <mutex>
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
#include "Common/GL/GLUtil.h" #include "Common/GL/GLUtil.h"
#include "Common/LinearDiskCache.h"
#include "VideoCommon/AsyncShaderCompiler.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 namespace OGL
{ {
@ -27,41 +19,6 @@ class OGLShader;
class GLVertexFormat; class GLVertexFormat;
class StreamBuffer; 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 struct SHADER
{ {
void Destroy() void Destroy()
@ -111,18 +68,6 @@ struct PipelineProgram
class ProgramShaderCache class ProgramShaderCache
{ {
public: 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 BindVertexFormat(const GLVertexFormat* vertex_format);
static void InvalidateVertexFormat(); static void InvalidateVertexFormat();
static void InvalidateLastProgram(); static void InvalidateLastProgram();
@ -140,114 +85,25 @@ public:
static void UploadConstants(); static void UploadConstants();
static void Init(); static void Init();
static void Reload();
static void Shutdown(); static void Shutdown();
static void CreateHeader(); 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* geometry_shader,
const OGLShader* pixel_shader); const OGLShader* pixel_shader);
static void ReleasePipelineProgram(const PipelineProgram* prog); static void ReleasePipelineProgram(const PipelineProgram* prog);
private: private:
template <typename UIDType>
class ProgramShaderCacheInserter : public LinearDiskCacheReader<UIDType, u8>
{
public:
ProgramShaderCacheInserter(std::map<UIDType, PCacheEntry>& 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<UIDType, PCacheEntry>& 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<cInterfaceBase> 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<SHADERUID, PCacheEntry> PCache;
typedef std::map<UBERSHADERUID, PCacheEntry> UberPCache;
typedef std::unordered_map<PipelineProgramKey, std::unique_ptr<PipelineProgram>, typedef std::unordered_map<PipelineProgramKey, std::unique_ptr<PipelineProgram>,
PipelineProgramKeyHash> PipelineProgramKeyHash>
PipelineProgramMap; PipelineProgramMap;
static void CreateAttributelessVAO(); 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 PipelineProgramMap s_pipeline_programs;
static UberPCache ubershaders; static std::mutex s_pipeline_program_lock;
static PipelineProgramMap pipelineprograms;
static PCacheEntry* last_entry;
static PCacheEntry* last_uber_entry;
static SHADERUID last_uid;
static UBERSHADERUID last_uber_uid;
static std::unique_ptr<SharedContextAsyncShaderCompiler> s_async_compiler;
static u32 s_ubo_buffer_size; static u32 s_ubo_buffer_size;
static s32 s_ubo_align; static s32 s_ubo_align;
@ -256,4 +112,12 @@ private:
static GLuint s_last_VAO; 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 } // namespace OGL

View File

@ -81,8 +81,8 @@ static bool s_efbCacheIsCleared = false;
static std::vector<u32> static std::vector<u32>
s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; // 2 for PeekZ and PeekColor 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, void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
GLsizei length, const char* message, const void* userParam) const char* message, const void* userParam)
{ {
const char* s_source; const char* s_source;
const char* s_type; const char* s_type;
@ -677,6 +677,10 @@ Renderer::Renderer()
g_Config.backend_info.bSupportsPaletteConversion && g_Config.backend_info.bSupportsPaletteConversion &&
g_Config.backend_info.bSupportsComputeShaders && g_ogl_config.bSupportsImageLoadStore; 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 (g_ogl_config.bSupportsDebug)
{ {
if (GLExtensions::Supports("GL_KHR_debug")) if (GLExtensions::Supports("GL_KHR_debug"))
@ -784,25 +788,7 @@ Renderer::Renderer()
glClearDepthf(1.0f); glClearDepthf(1.0f);
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
{ GLUtil::EnablePrimitiveRestart();
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);
}
}
}
IndexGenerator::Init(); IndexGenerator::Init();
UpdateActiveConfig(); UpdateActiveConfig();
@ -1286,8 +1272,11 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
glClear(clear_mask); 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 = bool useDualSource =
state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend && state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend &&
(!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha); (!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); 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. // 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. // Clean out old stuff from caches. It's not worth it to clean out the shader caches.
g_texture_cache->Cleanup(frameCount); g_texture_cache->Cleanup(frameCount);
ProgramShaderCache::RetrieveAsyncShaders();
RestoreAPIState(); RestoreAPIState();
@ -1479,8 +1468,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
g_sampler_cache->Clear(); g_sampler_cache->Clear();
// Invalidate shader cache when the host config changes. // Invalidate shader cache when the host config changes.
if (CheckForHostConfigChanges()) CheckForHostConfigChanges();
ProgramShaderCache::Reload();
// For testing zbuffer targets. // For testing zbuffer targets.
// Renderer::SetZBufferRender(); // Renderer::SetZBufferRender();
@ -1559,15 +1547,19 @@ void Renderer::RestoreAPIState()
glEnable(GL_CLIP_DISTANCE0); glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1); glEnable(GL_CLIP_DISTANCE1);
} }
BPFunctions::SetGenerationMode();
BPFunctions::SetScissor(); BPFunctions::SetScissor();
BPFunctions::SetViewport(); 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 // none, ccw, cw, ccw
if (state.cullmode != GenMode::CULL_NONE) if (state.cullmode != GenMode::CULL_NONE)
{ {
@ -1579,10 +1571,15 @@ void Renderer::ApplyRasterizationState(const RasterizationState& state)
{ {
glDisable(GL_CULL_FACE); 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, const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS}; GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
@ -1600,21 +1597,8 @@ void Renderer::ApplyDepthState(const DepthState& state)
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
} }
}
void Renderer::SetRasterizationState(const RasterizationState& state) m_current_depth_state = state;
{
ApplyRasterizationState(state);
}
void Renderer::SetDepthState(const DepthState& state)
{
ApplyDepthState(state);
}
void Renderer::SetBlendingState(const BlendingState& state)
{
ApplyBlendingState(state);
} }
void Renderer::SetPipeline(const AbstractPipeline* pipeline) 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 // Not all shader changes currently go through SetPipeline, so we can't
// test if the pipeline hasn't changed and skip these applications. Yet. // test if the pipeline hasn't changed and skip these applications. Yet.
m_graphics_pipeline = static_cast<const OGLPipeline*>(pipeline); m_graphics_pipeline = static_cast<const OGLPipeline*>(pipeline);
if (!m_graphics_pipeline)
return;
ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState()); ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState());
ApplyDepthState(m_graphics_pipeline->GetDepthState()); ApplyDepthState(m_graphics_pipeline->GetDepthState());
ApplyBlendingState(m_graphics_pipeline->GetBlendingState()); 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); glDispatchCompute(groups_x, groups_y, groups_z);
ProgramShaderCache::InvalidateLastProgram(); ProgramShaderCache::InvalidateLastProgram();
} }
std::unique_ptr<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCompiler()
{
return std::make_unique<SharedContextAsyncShaderCompiler>();
}
} }

View File

@ -105,10 +105,7 @@ public:
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
const ClearColor& color_value = {}, const ClearColor& color_value = {},
float depth_value = 0.0f) override; float depth_value = 0.0f) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override; void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
void SetDepthState(const DepthState& state) override;
void SetTexture(u32 index, const AbstractTexture* texture) override; void SetTexture(u32 index, const AbstractTexture* texture) override;
void SetSamplerState(u32 index, const SamplerState& state) override; void SetSamplerState(u32 index, const SamplerState& state) override;
void UnbindTexture(const AbstractTexture* texture) override; void UnbindTexture(const AbstractTexture* texture) override;
@ -142,6 +139,8 @@ public:
void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size, void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size,
u32 groups_x, u32 groups_y, u32 groups_z) override; u32 groups_x, u32 groups_y, u32 groups_z) override;
std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler() override;
private: private:
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
const TargetRectangle& targetPixelRc, const void* data); const TargetRectangle& targetPixelRc, const void* data);
@ -155,12 +154,15 @@ private:
void CheckForSurfaceChange(); void CheckForSurfaceChange();
void CheckForSurfaceResize(); void CheckForSurfaceResize();
void ApplyBlendingState(const BlendingState& state); void ApplyBlendingState(const BlendingState state, bool force = false);
void ApplyRasterizationState(const RasterizationState& state); void ApplyRasterizationState(const RasterizationState state, bool force = false);
void ApplyDepthState(const DepthState& state); void ApplyDepthState(const DepthState state, bool force = false);
void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size); void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size);
std::array<const AbstractTexture*, 8> m_bound_textures{}; std::array<const AbstractTexture*, 8> m_bound_textures{};
const OGLPipeline* m_graphics_pipeline = nullptr; const OGLPipeline* m_graphics_pipeline = nullptr;
RasterizationState m_current_rasterization_state = {};
DepthState m_current_depth_state = {};
BlendingState m_current_blend_state = {};
}; };
} }

View File

@ -162,8 +162,6 @@ void VertexManager::vFlush()
GLVertexFormat* nativeVertexFmt = (GLVertexFormat*)VertexLoaderManager::GetCurrentVertexFormat(); GLVertexFormat* nativeVertexFmt = (GLVertexFormat*)VertexLoaderManager::GetCurrentVertexFormat();
u32 stride = nativeVertexFmt->GetVertexStride(); u32 stride = nativeVertexFmt->GetVertexStride();
ProgramShaderCache::SetShader(m_current_primitive_type, nativeVertexFmt);
PrepareDrawBuffers(stride); PrepareDrawBuffers(stride);
// upload global constants // upload global constants
@ -174,7 +172,11 @@ void VertexManager::vFlush()
glEnable(GL_STENCIL_TEST); 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()) if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation())
{ {

View File

@ -160,7 +160,7 @@ bool VideoBackend::Initialize(void* window_handle)
InitBackendInfo(); InitBackendInfo();
InitializeShared(); InitializeShared();
InitInterface(); GLUtil::InitInterface();
GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT);
if (!GLInterface->Create(window_handle, g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer)) if (!GLInterface->Create(window_handle, g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer))
return false; return false;
@ -175,17 +175,20 @@ bool VideoBackend::Initialize(void* window_handle)
ProgramShaderCache::Init(); ProgramShaderCache::Init();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
g_sampler_cache = std::make_unique<SamplerCache>(); g_sampler_cache = std::make_unique<SamplerCache>();
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
static_cast<Renderer*>(g_renderer.get())->Init(); static_cast<Renderer*>(g_renderer.get())->Init();
TextureConverter::Init(); TextureConverter::Init();
BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight()); BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight());
return true; return g_shader_cache->Initialize();
} }
void VideoBackend::Shutdown() void VideoBackend::Shutdown()
{ {
g_shader_cache->Shutdown();
g_renderer->Shutdown(); g_renderer->Shutdown();
BoundingBox::Shutdown(); BoundingBox::Shutdown();
TextureConverter::Shutdown(); TextureConverter::Shutdown();
g_shader_cache.reset();
g_sampler_cache.reset(); g_sampler_cache.reset();
g_texture_cache.reset(); g_texture_cache.reset();
ProgramShaderCache::Shutdown(); ProgramShaderCache::Shutdown();

View File

@ -15,7 +15,7 @@ std::unique_ptr<SWOGLWindow> SWOGLWindow::s_instance;
void SWOGLWindow::Init(void* window_handle) void SWOGLWindow::Init(void* window_handle)
{ {
InitInterface(); GLUtil::InitInterface();
GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT);
if (!GLInterface->Create(window_handle)) if (!GLInterface->Create(window_handle))
{ {
@ -71,7 +71,7 @@ void SWOGLWindow::Prepare()
"#version 300 es\n" "#version 300 es\n"
"precision highp float;\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); glUseProgram(m_image_program);

View File

@ -73,6 +73,7 @@ void VideoSoftware::InitBackendInfo()
g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false;
g_Config.backend_info.bSupportsCopyToVram = false; g_Config.backend_info.bSupportsCopyToVram = false;
g_Config.backend_info.bSupportsFramebufferFetch = false; g_Config.backend_info.bSupportsFramebufferFetch = false;
g_Config.backend_info.bSupportsBackgroundCompiling = false;
// aamodes // aamodes
g_Config.backend_info.AAModes = {1}; g_Config.backend_info.AAModes = {1};
@ -96,11 +97,15 @@ bool VideoSoftware::Initialize(void* window_handle)
g_vertex_manager = std::make_unique<SWVertexLoader>(); g_vertex_manager = std::make_unique<SWVertexLoader>();
g_perf_query = std::make_unique<PerfQuery>(); g_perf_query = std::make_unique<PerfQuery>();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
return true; g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
return g_shader_cache->Initialize();
} }
void VideoSoftware::Shutdown() void VideoSoftware::Shutdown()
{ {
if (g_shader_cache)
g_shader_cache->Shutdown();
if (g_renderer) if (g_renderer)
g_renderer->Shutdown(); g_renderer->Shutdown();

View File

@ -48,8 +48,7 @@ enum DESCRIPTOR_SET_BIND_POINT
// - Standard // - Standard
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS) // - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
// - 8 combined image samplers (accessible from PS) // - 8 combined image samplers (accessible from PS)
// - BBox Enabled // - 1 SSBO accessible from PS if supported
// - Same as standard, plus a single SSBO accessible from PS
// - Push Constant // - Push Constant
// - Same as standard, plus 128 bytes of push constants, accessible from all stages. // - Same as standard, plus 128 bytes of push constants, accessible from all stages.
// - Texture Decoding // - Texture Decoding
@ -67,7 +66,6 @@ enum DESCRIPTOR_SET_BIND_POINT
enum PIPELINE_LAYOUT enum PIPELINE_LAYOUT
{ {
PIPELINE_LAYOUT_STANDARD, PIPELINE_LAYOUT_STANDARD,
PIPELINE_LAYOUT_BBOX,
PIPELINE_LAYOUT_PUSH_CONSTANT, PIPELINE_LAYOUT_PUSH_CONSTANT,
PIPELINE_LAYOUT_TEXTURE_CONVERSION, PIPELINE_LAYOUT_TEXTURE_CONVERSION,
PIPELINE_LAYOUT_UTILITY, PIPELINE_LAYOUT_UTILITY,

View File

@ -109,6 +109,9 @@ bool ObjectCache::CreateDescriptorSetLayouts()
static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = { static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = {
0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT}; 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[] = { static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = {
{UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, {UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
VK_SHADER_STAGE_FRAGMENT_BIT}, VK_SHADER_STAGE_FRAGMENT_BIT},
@ -139,7 +142,7 @@ bool ObjectCache::CreateDescriptorSetLayouts()
{7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT}, {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, {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings}, static_cast<u32>(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, {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, {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(compute_set_bindings)), compute_set_bindings}}; static_cast<u32>(ArraySize(compute_set_bindings)), compute_set_bindings}};
// Don't set the GS bit if geometry shaders aren't available.
if (!g_vulkan_context->SupportsGeometryShaders())
create_infos[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS].bindingCount--;
for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++) for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++)
{ {
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i], VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i],
@ -180,11 +187,10 @@ bool ObjectCache::CreatePipelineLayouts()
{ {
VkResult res; 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[] = { 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_PER_STAGE_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS], m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]}; 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, {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr}, static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
// BBox
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(bbox_sets)), bbox_sets, 0, nullptr},
// Push Constant // Push Constant
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range}, static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range},
@ -228,6 +230,10 @@ bool ObjectCache::CreatePipelineLayouts()
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}}; static_cast<u32>(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}};
// If bounding box is unsupported, don't bother with the SSBO descriptor set.
if (!g_vulkan_context->SupportsBoundingBox())
pipeline_layout_info[PIPELINE_LAYOUT_STANDARD].setLayoutCount--;
for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++) for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++)
{ {
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i], if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i],

View File

@ -224,7 +224,7 @@ std::tuple<VkBuffer, u32> Renderer::UpdateUtilityUniformBuffer(const void* unifo
void Renderer::SetPipeline(const AbstractPipeline* pipeline) void Renderer::SetPipeline(const AbstractPipeline* pipeline)
{ {
m_graphics_pipeline = static_cast<const VKPipeline*>(pipeline); StateTracker::GetInstance()->SetPipeline(static_cast<const VKPipeline*>(pipeline));
} }
void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices, 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. // Build commands.
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
m_graphics_pipeline->GetPipeline()); StateTracker::GetInstance()->GetPipeline()->GetVkPipeline());
if (vertex_buffer != VK_NULL_HANDLE) if (vertex_buffer != VK_NULL_HANDLE)
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset); 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. // Clean up stale textures.
TextureCache::GetInstance()->Cleanup(frameCount); TextureCache::GetInstance()->Cleanup(frameCount);
// Pull in now-ready async shaders.
g_shader_cache->RetrieveAsyncShaders();
} }
void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region) void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region)
@ -975,10 +972,8 @@ void Renderer::CheckForConfigChanges()
RecreateEFBFramebuffer(); RecreateEFBFramebuffer();
RecompileShaders(); RecompileShaders();
FramebufferManager::GetInstance()->RecompileShaders(); FramebufferManager::GetInstance()->RecompileShaders();
g_shader_cache->ReloadShaderAndPipelineCaches(); g_shader_cache->ReloadPipelineCache();
g_shader_cache->RecompileSharedShaders(); 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. // 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()); FramebufferManager::GetInstance()->GetEFBClearRenderPass());
StateTracker::GetInstance()->SetFramebuffer( StateTracker::GetInstance()->SetFramebuffer(
FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size); FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size);
StateTracker::GetInstance()->SetMultisamplingstate(
FramebufferManager::GetInstance()->GetEFBMultisamplingState());
m_current_framebuffer = nullptr; m_current_framebuffer = nullptr;
m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth(); m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth();
m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight(); m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight();
@ -1125,21 +1118,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
num_clear_values); 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) void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
{ {
// Texture should always be in SHADER_READ_ONLY layout prior to use. // Texture should always be in SHADER_READ_ONLY layout prior to use.

View File

@ -77,10 +77,7 @@ public:
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
const ClearColor& color_value = {}, const ClearColor& color_value = {},
float depth_value = 0.0f) override; float depth_value = 0.0f) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override; void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
void SetDepthState(const DepthState& state) override;
void SetTexture(u32 index, const AbstractTexture* texture) override; void SetTexture(u32 index, const AbstractTexture* texture) override;
void SetSamplerState(u32 index, const SamplerState& state) override; void SetSamplerState(u32 index, const SamplerState& state) override;
void UnbindTexture(const AbstractTexture* texture) override; void UnbindTexture(const AbstractTexture* texture) override;
@ -135,6 +132,5 @@ private:
// Shaders used for clear/blit. // Shaders used for clear/blit.
VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE; VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE;
const VKPipeline* m_graphics_pipeline = nullptr;
}; };
} }

View File

@ -23,12 +23,7 @@
#include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VertexFormat.h"
#include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoBackends/Vulkan/VulkanContext.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/Statistics.h" #include "VideoCommon/Statistics.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexLoaderManager.h"
namespace Vulkan namespace Vulkan
{ {
@ -41,7 +36,6 @@ ShaderCache::ShaderCache()
ShaderCache::~ShaderCache() ShaderCache::~ShaderCache()
{ {
DestroyPipelineCache(); DestroyPipelineCache();
DestroyShaderCaches();
DestroySharedShaders(); DestroySharedShaders();
} }
@ -49,7 +43,6 @@ bool ShaderCache::Initialize()
{ {
if (g_ActiveConfig.bShaderCache) if (g_ActiveConfig.bShaderCache)
{ {
LoadShaderCaches();
if (!LoadPipelineCache()) if (!LoadPipelineCache())
return false; return false;
} }
@ -62,21 +55,11 @@ bool ShaderCache::Initialize()
if (!CompileSharedShaders()) if (!CompileSharedShaders())
return false; return false;
m_async_shader_compiler = std::make_unique<VideoCommon::AsyncShaderCompiler>();
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ?
g_ActiveConfig.GetShaderPrecompilerThreads() :
g_ActiveConfig.GetShaderCompilerThreads());
return true; return true;
} }
void ShaderCache::Shutdown() 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) if (g_ActiveConfig.bShaderCache && m_pipeline_cache != VK_NULL_HANDLE)
SavePipelineCache(); SavePipelineCache();
} }
@ -392,40 +375,14 @@ VkPipeline ShaderCache::CreatePipeline(const PipelineInfo& info)
} }
VkPipeline ShaderCache::GetPipeline(const PipelineInfo& info) VkPipeline ShaderCache::GetPipeline(const PipelineInfo& info)
{
return GetPipelineWithCacheResult(info).first;
}
std::pair<VkPipeline, bool> ShaderCache::GetPipelineWithCacheResult(const PipelineInfo& info)
{ {
auto iter = m_pipeline_objects.find(info); auto iter = m_pipeline_objects.find(info);
if (iter != m_pipeline_objects.end()) if (iter != m_pipeline_objects.end())
{ return iter->second;
// 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);
}
VkPipeline pipeline = CreatePipeline(info); VkPipeline pipeline = CreatePipeline(info);
m_pipeline_objects.emplace(info, std::make_pair(pipeline, false)); m_pipeline_objects.emplace(info, pipeline);
_assert_(pipeline != VK_NULL_HANDLE); return pipeline;
return {pipeline, false};
}
std::pair<std::pair<VkPipeline, bool>, bool>
ShaderCache::GetPipelineWithCacheResultAsync(const PipelineInfo& info)
{
auto iter = m_pipeline_objects.find(info);
if (iter != m_pipeline_objects.end())
return std::make_pair(iter->second, true);
// Kick a job off.
m_async_shader_compiler->QueueWorkItem(
m_async_shader_compiler->CreateWorkItem<PipelineCompilerWorkItem>(info));
m_pipeline_objects.emplace(info, std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true));
return std::make_pair(std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true), false);
} }
VkPipeline ShaderCache::CreateComputePipeline(const ComputePipelineInfo& info) VkPipeline ShaderCache::CreateComputePipeline(const ComputePipelineInfo& info)
@ -465,11 +422,10 @@ VkPipeline ShaderCache::GetComputePipeline(const ComputePipelineInfo& info)
void ShaderCache::ClearPipelineCache() void ShaderCache::ClearPipelineCache()
{ {
// TODO: Stop any async compiling happening.
for (const auto& it : m_pipeline_objects) for (const auto& it : m_pipeline_objects)
{ {
if (it.second.first != VK_NULL_HANDLE) if (it.second != VK_NULL_HANDLE)
vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second.first, nullptr); vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr);
} }
m_pipeline_objects.clear(); m_pipeline_objects.clear();
@ -673,266 +629,6 @@ void ShaderCache::SavePipelineCache()
disk_cache.Close(); disk_cache.Close();
} }
// Cache inserter that is called back when reading from the file
template <typename Uid>
struct ShaderCacheReader : public LinearDiskCacheReader<Uid, u32>
{
ShaderCacheReader(std::map<Uid, std::pair<VkShaderModule, bool>>& shader_map)
: m_shader_map(shader_map)
{
}
void Read(const Uid& key, const u32* value, u32 value_size) override
{
// We don't insert null modules into the shader map since creation could succeed later on.
// e.g. we're generating bad code, but fix this in a later version, and for some reason
// the cache is not invalidated.
VkShaderModule module = Util::CreateShaderModule(value, value_size);
if (module == VK_NULL_HANDLE)
return;
m_shader_map.emplace(key, std::make_pair(module, false));
}
std::map<Uid, std::pair<VkShaderModule, bool>>& m_shader_map;
};
void ShaderCache::LoadShaderCaches()
{
ShaderCacheReader<VertexShaderUid> vs_reader(m_vs_cache.shader_map);
m_vs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "VS", true, true),
vs_reader);
ShaderCacheReader<PixelShaderUid> ps_reader(m_ps_cache.shader_map);
m_ps_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "PS", true, true),
ps_reader);
if (g_vulkan_context->SupportsGeometryShaders())
{
ShaderCacheReader<GeometryShaderUid> gs_reader(m_gs_cache.shader_map);
m_gs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "GS", true, true),
gs_reader);
}
ShaderCacheReader<UberShader::VertexShaderUid> uber_vs_reader(m_uber_vs_cache.shader_map);
m_uber_vs_cache.disk_cache.OpenAndRead(
GetDiskShaderCacheFileName(APIType::Vulkan, "UberVS", false, true), uber_vs_reader);
ShaderCacheReader<UberShader::PixelShaderUid> uber_ps_reader(m_uber_ps_cache.shader_map);
m_uber_ps_cache.disk_cache.OpenAndRead(
GetDiskShaderCacheFileName(APIType::Vulkan, "UberPS", false, true), uber_ps_reader);
SETSTAT(stats.numPixelShadersCreated, static_cast<int>(m_ps_cache.shader_map.size()));
SETSTAT(stats.numPixelShadersAlive, static_cast<int>(m_ps_cache.shader_map.size()));
SETSTAT(stats.numVertexShadersCreated, static_cast<int>(m_vs_cache.shader_map.size()));
SETSTAT(stats.numVertexShadersAlive, static_cast<int>(m_vs_cache.shader_map.size()));
}
template <typename T>
static void DestroyShaderCache(T& cache)
{
cache.disk_cache.Sync();
cache.disk_cache.Close();
for (const auto& it : cache.shader_map)
{
if (it.second.first != VK_NULL_HANDLE)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second.first, nullptr);
}
cache.shader_map.clear();
}
void ShaderCache::DestroyShaderCaches()
{
DestroyShaderCache(m_vs_cache);
DestroyShaderCache(m_ps_cache);
if (g_vulkan_context->SupportsGeometryShaders())
DestroyShaderCache(m_gs_cache);
DestroyShaderCache(m_uber_vs_cache);
DestroyShaderCache(m_uber_ps_cache);
SETSTAT(stats.numPixelShadersCreated, 0);
SETSTAT(stats.numPixelShadersAlive, 0);
SETSTAT(stats.numVertexShadersCreated, 0);
SETSTAT(stats.numVertexShadersAlive, 0);
}
VkShaderModule ShaderCache::GetVertexShaderForUid(const VertexShaderUid& uid)
{
auto it = m_vs_cache.shader_map.find(uid);
if (it != m_vs_cache.shader_map.end())
{
// If it's pending, compile it synchronously.
if (!it->second.second)
return it->second.first;
else
m_vs_cache.shader_map.erase(it);
}
// Not in the cache, so compile the shader.
ShaderCompiler::SPIRVCodeVector spv;
VkShaderModule module = VK_NULL_HANDLE;
ShaderCode source_code =
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
source_code.GetBuffer().length()))
{
module = Util::CreateShaderModule(spv.data(), spv.size());
// Append to shader cache if it created successfully.
if (module != VK_NULL_HANDLE)
{
m_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
INCSTAT(stats.numVertexShadersCreated);
INCSTAT(stats.numVertexShadersAlive);
}
}
// We still insert null entries to prevent further compilation attempts.
m_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
return module;
}
VkShaderModule ShaderCache::GetGeometryShaderForUid(const GeometryShaderUid& uid)
{
_assert_(g_vulkan_context->SupportsGeometryShaders());
auto it = m_gs_cache.shader_map.find(uid);
if (it != m_gs_cache.shader_map.end())
{
// If it's pending, compile it synchronously.
if (!it->second.second)
return it->second.first;
else
m_gs_cache.shader_map.erase(it);
}
// Not in the cache, so compile the shader.
ShaderCompiler::SPIRVCodeVector spv;
VkShaderModule module = VK_NULL_HANDLE;
ShaderCode source_code =
GenerateGeometryShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
if (ShaderCompiler::CompileGeometryShader(&spv, source_code.GetBuffer().c_str(),
source_code.GetBuffer().length()))
{
module = Util::CreateShaderModule(spv.data(), spv.size());
// Append to shader cache if it created successfully.
if (module != VK_NULL_HANDLE)
m_gs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
}
// We still insert null entries to prevent further compilation attempts.
m_gs_cache.shader_map.emplace(uid, std::make_pair(module, false));
return module;
}
VkShaderModule ShaderCache::GetPixelShaderForUid(const PixelShaderUid& uid)
{
auto it = m_ps_cache.shader_map.find(uid);
if (it != m_ps_cache.shader_map.end())
{
// If it's pending, compile it synchronously.
if (!it->second.second)
return it->second.first;
else
m_ps_cache.shader_map.erase(it);
}
// Not in the cache, so compile the shader.
ShaderCompiler::SPIRVCodeVector spv;
VkShaderModule module = VK_NULL_HANDLE;
ShaderCode source_code =
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
source_code.GetBuffer().length()))
{
module = Util::CreateShaderModule(spv.data(), spv.size());
// Append to shader cache if it created successfully.
if (module != VK_NULL_HANDLE)
{
m_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
INCSTAT(stats.numPixelShadersCreated);
INCSTAT(stats.numPixelShadersAlive);
}
}
// We still insert null entries to prevent further compilation attempts.
m_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
return module;
}
VkShaderModule ShaderCache::GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid)
{
auto it = m_uber_vs_cache.shader_map.find(uid);
if (it != m_uber_vs_cache.shader_map.end())
{
// If it's pending, compile it synchronously.
if (!it->second.second)
return it->second.first;
else
m_uber_vs_cache.shader_map.erase(it);
}
// Not in the cache, so compile the shader.
ShaderCompiler::SPIRVCodeVector spv;
VkShaderModule module = VK_NULL_HANDLE;
ShaderCode source_code = UberShader::GenVertexShader(
APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
source_code.GetBuffer().length()))
{
module = Util::CreateShaderModule(spv.data(), spv.size());
// Append to shader cache if it created successfully.
if (module != VK_NULL_HANDLE)
{
m_uber_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
INCSTAT(stats.numVertexShadersCreated);
INCSTAT(stats.numVertexShadersAlive);
}
}
// We still insert null entries to prevent further compilation attempts.
m_uber_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
return module;
}
VkShaderModule ShaderCache::GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid)
{
auto it = m_uber_ps_cache.shader_map.find(uid);
if (it != m_uber_ps_cache.shader_map.end())
{
// If it's pending, compile it synchronously.
if (!it->second.second)
return it->second.first;
else
m_uber_ps_cache.shader_map.erase(it);
}
// Not in the cache, so compile the shader.
ShaderCompiler::SPIRVCodeVector spv;
VkShaderModule module = VK_NULL_HANDLE;
ShaderCode source_code =
UberShader::GenPixelShader(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
source_code.GetBuffer().length()))
{
module = Util::CreateShaderModule(spv.data(), spv.size());
// Append to shader cache if it created successfully.
if (module != VK_NULL_HANDLE)
{
m_uber_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
INCSTAT(stats.numPixelShadersCreated);
INCSTAT(stats.numPixelShadersAlive);
}
}
// We still insert null entries to prevent further compilation attempts.
m_uber_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
return module;
}
void ShaderCache::RecompileSharedShaders() void ShaderCache::RecompileSharedShaders()
{ {
DestroySharedShaders(); DestroySharedShaders();
@ -940,27 +636,15 @@ void ShaderCache::RecompileSharedShaders()
PanicAlert("Failed to recompile shared shaders."); PanicAlert("Failed to recompile shared shaders.");
} }
void ShaderCache::ReloadShaderAndPipelineCaches() void ShaderCache::ReloadPipelineCache()
{ {
m_async_shader_compiler->WaitUntilCompletion();
m_async_shader_compiler->RetrieveWorkItems();
SavePipelineCache(); SavePipelineCache();
DestroyShaderCaches();
DestroyPipelineCache(); DestroyPipelineCache();
if (g_ActiveConfig.bShaderCache) if (g_ActiveConfig.bShaderCache)
{
LoadShaderCaches();
LoadPipelineCache(); LoadPipelineCache();
}
else else
{
CreatePipelineCache(); CreatePipelineCache();
}
if (g_ActiveConfig.CanPrecompileUberShaders())
PrecompileUberShaders();
} }
std::string ShaderCache::GetUtilityShaderHeader() const std::string ShaderCache::GetUtilityShaderHeader() const
@ -1160,203 +844,4 @@ void ShaderCache::DestroySharedShaders()
DestroyShader(m_screen_quad_geometry_shader); DestroyShader(m_screen_quad_geometry_shader);
DestroyShader(m_passthrough_geometry_shader); DestroyShader(m_passthrough_geometry_shader);
} }
void ShaderCache::CreateDummyPipeline(const UberShader::VertexShaderUid& vuid,
const GeometryShaderUid& guid,
const UberShader::PixelShaderUid& puid)
{
PortableVertexDeclaration vertex_decl;
std::memset(&vertex_decl, 0, sizeof(vertex_decl));
PipelineInfo pinfo;
pinfo.vertex_format =
static_cast<const VertexFormat*>(VertexLoaderManager::GetUberVertexFormat(vertex_decl));
pinfo.pipeline_layout = g_object_cache->GetPipelineLayout(
g_ActiveConfig.bBBoxEnable && g_ActiveConfig.BBoxUseFragmentShaderImplementation() ?
PIPELINE_LAYOUT_BBOX :
PIPELINE_LAYOUT_STANDARD);
pinfo.vs = GetVertexUberShaderForUid(vuid);
pinfo.gs = (!guid.GetUidData()->IsPassthrough() && g_vulkan_context->SupportsGeometryShaders()) ?
GetGeometryShaderForUid(guid) :
VK_NULL_HANDLE;
pinfo.ps = GetPixelUberShaderForUid(puid);
pinfo.render_pass = FramebufferManager::GetInstance()->GetEFBLoadRenderPass();
pinfo.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex;
pinfo.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex;
pinfo.blend_state.hex = RenderState::GetNoBlendingBlendState().hex;
pinfo.multisampling_state.hex = FramebufferManager::GetInstance()->GetEFBMultisamplingState().hex;
pinfo.rasterization_state.primitive =
static_cast<PrimitiveType>(guid.GetUidData()->primitive_type);
GetPipelineWithCacheResultAsync(pinfo);
}
void ShaderCache::PrecompileUberShaders()
{
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
// UIDs must have compatible texgens, a mismatching combination will never be queried.
if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
return;
EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens)
return;
CreateDummyPipeline(vuid, guid, puid);
});
});
});
WaitForBackgroundCompilesToComplete();
// Switch to the runtime/background thread config.
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
}
void ShaderCache::WaitForBackgroundCompilesToComplete()
{
m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
static_cast<int>(completed), static_cast<int>(total));
});
m_async_shader_compiler->RetrieveWorkItems();
Host_UpdateProgressDialog("", -1, -1);
}
void ShaderCache::RetrieveAsyncShaders()
{
m_async_shader_compiler->RetrieveWorkItems();
}
std::pair<VkShaderModule, bool> ShaderCache::GetVertexShaderForUidAsync(const VertexShaderUid& uid)
{
auto it = m_vs_cache.shader_map.find(uid);
if (it != m_vs_cache.shader_map.end())
return it->second;
// Kick a compile job off.
m_async_shader_compiler->QueueWorkItem(
m_async_shader_compiler->CreateWorkItem<VertexShaderCompilerWorkItem>(uid));
m_vs_cache.shader_map.emplace(uid,
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
}
std::pair<VkShaderModule, bool> ShaderCache::GetPixelShaderForUidAsync(const PixelShaderUid& uid)
{
auto it = m_ps_cache.shader_map.find(uid);
if (it != m_ps_cache.shader_map.end())
return it->second;
// Kick a compile job off.
m_async_shader_compiler->QueueWorkItem(
m_async_shader_compiler->CreateWorkItem<PixelShaderCompilerWorkItem>(uid));
m_ps_cache.shader_map.emplace(uid,
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
}
bool ShaderCache::VertexShaderCompilerWorkItem::Compile()
{
ShaderCode code =
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
if (!ShaderCompiler::CompileVertexShader(&m_spirv, code.GetBuffer().c_str(),
code.GetBuffer().length()))
return true;
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
return true;
}
void ShaderCache::VertexShaderCompilerWorkItem::Retrieve()
{
auto it = g_shader_cache->m_vs_cache.shader_map.find(m_uid);
if (it == g_shader_cache->m_vs_cache.shader_map.end())
{
g_shader_cache->m_vs_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
static_cast<u32>(m_spirv.size()));
return;
}
// The main thread may have also compiled this shader.
if (!it->second.second)
{
if (m_module != VK_NULL_HANDLE)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
return;
}
// No longer pending.
it->second.first = m_module;
it->second.second = false;
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
static_cast<u32>(m_spirv.size()));
}
bool ShaderCache::PixelShaderCompilerWorkItem::Compile()
{
ShaderCode code =
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
if (!ShaderCompiler::CompileFragmentShader(&m_spirv, code.GetBuffer().c_str(),
code.GetBuffer().length()))
return true;
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
return true;
}
void ShaderCache::PixelShaderCompilerWorkItem::Retrieve()
{
auto it = g_shader_cache->m_ps_cache.shader_map.find(m_uid);
if (it == g_shader_cache->m_ps_cache.shader_map.end())
{
g_shader_cache->m_ps_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
static_cast<u32>(m_spirv.size()));
return;
}
// The main thread may have also compiled this shader.
if (!it->second.second)
{
if (m_module != VK_NULL_HANDLE)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
return;
}
// No longer pending.
it->second.first = m_module;
it->second.second = false;
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
static_cast<u32>(m_spirv.size()));
}
bool ShaderCache::PipelineCompilerWorkItem::Compile()
{
m_pipeline = g_shader_cache->CreatePipeline(m_info);
return true;
}
void ShaderCache::PipelineCompilerWorkItem::Retrieve()
{
auto it = g_shader_cache->m_pipeline_objects.find(m_info);
if (it == g_shader_cache->m_pipeline_objects.end())
{
g_shader_cache->m_pipeline_objects.emplace(m_info, std::make_pair(m_pipeline, false));
return;
}
// The main thread may have also compiled this shader.
if (!it->second.second)
{
if (m_pipeline != VK_NULL_HANDLE)
vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr);
return;
}
// No longer pending.
it->second.first = m_pipeline;
it->second.second = false;
}
} }

View File

@ -19,13 +19,7 @@
#include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/ShaderCompiler.h" #include "VideoBackends/Vulkan/ShaderCompiler.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexShaderGen.h"
namespace Vulkan namespace Vulkan
{ {
@ -33,10 +27,6 @@ class CommandBufferManager;
class VertexFormat; class VertexFormat;
class StreamBuffer; class StreamBuffer;
class CommandBufferManager;
class VertexFormat;
class StreamBuffer;
struct PipelineInfo struct PipelineInfo
{ {
// These are packed in descending order of size, to avoid any padding so that the structure // 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. // Get utility shader header based on current config.
std::string GetUtilityShaderHeader() const; std::string GetUtilityShaderHeader() const;
// Accesses ShaderGen shader caches
VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid);
VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid);
VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid);
// Ubershader caches
VkShaderModule GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid);
VkShaderModule GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid);
// Accesses ShaderGen shader caches asynchronously
std::pair<VkShaderModule, bool> GetVertexShaderForUidAsync(const VertexShaderUid& uid);
std::pair<VkShaderModule, bool> GetPixelShaderForUidAsync(const PixelShaderUid& uid);
// Perform at startup, create descriptor layouts, compiles all static shaders. // Perform at startup, create descriptor layouts, compiles all static shaders.
bool Initialize(); bool Initialize();
void Shutdown(); void Shutdown();
@ -112,13 +89,6 @@ public:
// Find a pipeline by the specified description, if not found, attempts to create it. // Find a pipeline by the specified description, if not found, attempts to create it.
VkPipeline GetPipeline(const PipelineInfo& info); VkPipeline GetPipeline(const PipelineInfo& info);
// Find a pipeline by the specified description, if not found, attempts to create it. If this
// resulted in a pipeline being created, the second field of the return value will be false,
// otherwise for a cache hit it will be true.
std::pair<VkPipeline, bool> GetPipelineWithCacheResult(const PipelineInfo& info);
std::pair<std::pair<VkPipeline, bool>, bool>
GetPipelineWithCacheResultAsync(const PipelineInfo& info);
// Creates a compute pipeline, and does not track the handle. // Creates a compute pipeline, and does not track the handle.
VkPipeline CreateComputePipeline(const ComputePipelineInfo& info); VkPipeline CreateComputePipeline(const ComputePipelineInfo& info);
@ -139,47 +109,22 @@ public:
void RecompileSharedShaders(); void RecompileSharedShaders();
// Reload pipeline cache. This will destroy all pipelines. // Reload pipeline cache. This will destroy all pipelines.
void ReloadShaderAndPipelineCaches(); void ReloadPipelineCache();
// Shared shader accessors // Shared shader accessors
VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; } VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; }
VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; } VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; }
VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; } VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; }
VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; } VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; }
void PrecompileUberShaders();
void WaitForBackgroundCompilesToComplete();
void RetrieveAsyncShaders();
private: private:
bool CreatePipelineCache(); bool CreatePipelineCache();
bool LoadPipelineCache(); bool LoadPipelineCache();
bool ValidatePipelineCache(const u8* data, size_t data_length); bool ValidatePipelineCache(const u8* data, size_t data_length);
void DestroyPipelineCache(); void DestroyPipelineCache();
void LoadShaderCaches();
void DestroyShaderCaches();
bool CompileSharedShaders(); bool CompileSharedShaders();
void DestroySharedShaders(); void DestroySharedShaders();
// We generate a dummy pipeline with some defaults in the blend/depth states, std::unordered_map<PipelineInfo, VkPipeline, PipelineInfoHash> m_pipeline_objects;
// that way the driver is forced to compile something (looking at you, NVIDIA).
// It can then hopefully re-use part of this pipeline for others in the future.
void CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, const GeometryShaderUid& guid,
const UberShader::PixelShaderUid& puid);
template <typename Uid>
struct ShaderModuleCache
{
std::map<Uid, std::pair<VkShaderModule, bool>> shader_map;
LinearDiskCache<Uid, u32> disk_cache;
};
ShaderModuleCache<VertexShaderUid> m_vs_cache;
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
ShaderModuleCache<PixelShaderUid> m_ps_cache;
ShaderModuleCache<UberShader::VertexShaderUid> m_uber_vs_cache;
ShaderModuleCache<UberShader::PixelShaderUid> m_uber_ps_cache;
std::unordered_map<PipelineInfo, std::pair<VkPipeline, bool>, PipelineInfoHash>
m_pipeline_objects;
std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash> std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash>
m_compute_pipeline_objects; m_compute_pipeline_objects;
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE; VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
@ -190,45 +135,6 @@ private:
VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE; VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE;
VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE; VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE;
VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE; VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE;
std::unique_ptr<VideoCommon::AsyncShaderCompiler> m_async_shader_compiler;
// TODO: Use templates to reduce the number of these classes.
class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid) : m_uid(uid) {}
bool Compile() override;
void Retrieve() override;
private:
VertexShaderUid m_uid;
ShaderCompiler::SPIRVCodeVector m_spirv;
VkShaderModule m_module = VK_NULL_HANDLE;
};
class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid) : m_uid(uid) {}
bool Compile() override;
void Retrieve() override;
private:
PixelShaderUid m_uid;
ShaderCompiler::SPIRVCodeVector m_spirv;
VkShaderModule m_module = VK_NULL_HANDLE;
};
class PipelineCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
explicit PipelineCompilerWorkItem(const PipelineInfo& info) : m_info(info) {}
bool Compile() override;
void Retrieve() override;
private:
PipelineInfo m_info;
VkPipeline m_pipeline;
};
}; };
extern std::unique_ptr<ShaderCache> g_shader_cache; extern std::unique_ptr<ShaderCache> g_shader_cache;

View File

@ -15,6 +15,7 @@
#include "VideoBackends/Vulkan/ShaderCache.h" #include "VideoBackends/Vulkan/ShaderCache.h"
#include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VKPipeline.h"
#include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VertexFormat.h"
#include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoBackends/Vulkan/VulkanContext.h"
@ -53,12 +54,6 @@ void StateTracker::DestroyInstance()
bool StateTracker::Initialize() 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 // Initialize all samplers to point by default
for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++) for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++)
{ {
@ -97,122 +92,6 @@ bool StateTracker::Initialize()
return true; return true;
} }
void StateTracker::InvalidateShaderPointers()
{
// Clear UIDs, forcing a false match next time.
m_vs_uid = {};
m_gs_uid = {};
m_ps_uid = {};
// Invalidate shader pointers.
m_pipeline_state.vs = VK_NULL_HANDLE;
m_pipeline_state.gs = VK_NULL_HANDLE;
m_pipeline_state.ps = VK_NULL_HANDLE;
}
void StateTracker::ReloadPipelineUIDCache()
{
class PipelineInserter final : public LinearDiskCacheReader<SerializedPipelineUID, u32>
{
public:
explicit PipelineInserter(StateTracker* this_ptr_) : this_ptr(this_ptr_) {}
void Read(const SerializedPipelineUID& key, const u32* value, u32 value_size)
{
this_ptr->PrecachePipelineUID(key);
}
private:
StateTracker* this_ptr;
};
m_uid_cache.Sync();
m_uid_cache.Close();
// UID caches don't contain any host state, so use a single uid cache per gameid.
std::string filename = GetDiskShaderCacheFileName(APIType::Vulkan, "PipelineUID", true, false);
if (g_ActiveConfig.bShaderCache)
{
PipelineInserter inserter(this);
m_uid_cache.OpenAndRead(filename, inserter);
}
// If we were using background compilation, ensure everything is ready before continuing.
if (g_ActiveConfig.bBackgroundShaderCompiling)
g_shader_cache->WaitForBackgroundCompilesToComplete();
}
void StateTracker::AppendToPipelineUIDCache(const PipelineInfo& info)
{
SerializedPipelineUID sinfo;
sinfo.blend_state_bits = info.blend_state.hex;
sinfo.rasterizer_state_bits = info.rasterization_state.hex;
sinfo.depth_state_bits = info.depth_state.hex;
sinfo.vertex_decl = m_pipeline_state.vertex_format->GetVertexDeclaration();
sinfo.vs_uid = m_vs_uid;
sinfo.gs_uid = m_gs_uid;
sinfo.ps_uid = m_ps_uid;
u32 dummy_value = 0;
m_uid_cache.Append(sinfo, &dummy_value, 1);
}
bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
{
PipelineInfo pinfo = {};
// Need to create the vertex declaration first, rather than deferring to when a game creates a
// vertex loader that uses this format, since we need it to create a pipeline.
pinfo.vertex_format =
static_cast<VertexFormat*>(VertexLoaderManager::GetOrCreateMatchingFormat(uid.vertex_decl));
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
pinfo.vs = g_shader_cache->GetVertexShaderForUid(uid.vs_uid);
if (pinfo.vs == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Failed to get vertex shader from cached UID.");
return false;
}
if (g_vulkan_context->SupportsGeometryShaders() && !uid.gs_uid.GetUidData()->IsPassthrough())
{
pinfo.gs = g_shader_cache->GetGeometryShaderForUid(uid.gs_uid);
if (pinfo.gs == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Failed to get geometry shader from cached UID.");
return false;
}
}
pinfo.ps = g_shader_cache->GetPixelShaderForUid(uid.ps_uid);
if (pinfo.ps == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Failed to get pixel shader from cached UID.");
return false;
}
pinfo.render_pass = m_load_render_pass;
pinfo.rasterization_state.hex = uid.rasterizer_state_bits;
pinfo.depth_state.hex = uid.depth_state_bits;
pinfo.blend_state.hex = uid.blend_state_bits;
pinfo.multisampling_state.hex = m_pipeline_state.multisampling_state.hex;
if (g_ActiveConfig.bBackgroundShaderCompiling)
{
// Use async for multithreaded compilation.
g_shader_cache->GetPipelineWithCacheResultAsync(pinfo);
}
else
{
VkPipeline pipeline = g_shader_cache->GetPipeline(pinfo);
if (pipeline == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Failed to get pipeline from cached UID.");
return false;
}
}
// We don't need to do anything with this pipeline, just make sure it exists.
return true;
}
void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset) void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset)
{ {
if (m_vertex_buffer == buffer && m_vertex_buffer_offset == 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. // Should not be changed within a render pass.
_assert_(!InRenderPass()); _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_load_render_pass = load_render_pass;
m_clear_render_pass = clear_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; 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; return;
m_vertex_format = vertex_format; const bool new_usage =
UpdatePipelineVertexFormat(); pipeline && (!m_pipeline || m_pipeline->GetUsage() != pipeline->GetUsage());
}
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;
m_pipeline = pipeline;
m_dirty_flags |= DIRTY_FLAG_PIPELINE; m_dirty_flags |= DIRTY_FLAG_PIPELINE;
if (new_usage)
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
} }
void StateTracker::UpdateVertexShaderConstants() void StateTracker::UpdateVertexShaderConstants()
@ -450,20 +170,6 @@ void StateTracker::UpdateVertexShaderConstants()
void StateTracker::UpdateGeometryShaderConstants() 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()) if (!GeometryShaderManager::dirty || !ReserveConstantStorage())
return; return;
@ -614,15 +320,6 @@ void StateTracker::SetSampler(size_t index, VkSampler sampler)
m_dirty_flags |= DIRTY_FLAG_PS_SAMPLERS; 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) void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range)
{ {
if (m_bindings.ps_ssbo.buffer == buffer && m_bindings.ps_ssbo.offset == offset && 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.buffer = buffer;
m_bindings.ps_ssbo.offset = offset; m_bindings.ps_ssbo.offset = offset;
m_bindings.ps_ssbo.range = range; m_bindings.ps_ssbo.range = range;
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
// Defer descriptor update until bbox is actually enabled.
if (IsSSBODescriptorRequired())
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
} }
void StateTracker::UnbindTexture(VkImageView view) void StateTracker::UnbindTexture(VkImageView view)
@ -653,10 +347,6 @@ void StateTracker::InvalidateDescriptorSets()
{ {
m_descriptor_sets.fill(VK_NULL_HANDLE); m_descriptor_sets.fill(VK_NULL_HANDLE);
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS; 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() void StateTracker::InvalidateConstants()
@ -669,9 +359,8 @@ void StateTracker::InvalidateConstants()
void StateTracker::SetPendingRebind() void StateTracker::SetPendingRebind()
{ {
m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS | DIRTY_FLAG_DESCRIPTOR_SET_BINDING | m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS | DIRTY_FLAG_DESCRIPTOR_SET_BINDING |
DIRTY_FLAG_PIPELINE_BINDING | DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT |
DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_PIPELINE;
DIRTY_FLAG_PIPELINE;
} }
void StateTracker::BeginRenderPass() void StateTracker::BeginRenderPass()
@ -743,17 +432,14 @@ void StateTracker::SetScissor(const VkRect2D& scissor)
bool StateTracker::Bind(bool rebind_all /*= false*/) 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. // Check the render area if we were in a clear pass.
if (m_current_render_pass == m_clear_render_pass && !IsViewportWithinRenderArea()) if (m_current_render_pass == m_clear_render_pass && !IsViewportWithinRenderArea())
EndRenderPass(); 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 // Get a new descriptor set if any parts have changed
if (m_dirty_flags & DIRTY_FLAG_ALL_DESCRIPTOR_SETS && !UpdateDescriptorSet()) 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) if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER || rebind_all)
vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type); vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type);
if (m_dirty_flags & DIRTY_FLAG_PIPELINE_BINDING || rebind_all) if (m_dirty_flags & DIRTY_FLAG_PIPELINE || rebind_all)
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_object); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline());
if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SET_BINDING || rebind_all) if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SET_BINDING || rebind_all)
{ {
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 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_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS,
m_bindings.uniform_buffer_offsets.data()); m_bindings.uniform_buffer_offsets.data());
} }
else if (m_dirty_flags & DIRTY_FLAG_DYNAMIC_OFFSETS) else if (m_dirty_flags & DIRTY_FLAG_DYNAMIC_OFFSETS)
{ {
vkCmdBindDescriptorSets( 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, DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1,
&m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS], &m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS],
NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data()); NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data());
@ -933,105 +619,6 @@ void StateTracker::EndClearRenderPass()
EndRenderPass(); EndRenderPass();
} }
VkPipeline StateTracker::GetPipelineAndCacheUID()
{
// We can't cache ubershader uids, only normal shader uids.
if (g_ActiveConfig.CanBackgroundCompileShaders() && !m_using_ubershaders)
{
// Append to UID cache if it is a new pipeline.
auto result = g_shader_cache->GetPipelineWithCacheResultAsync(m_pipeline_state);
if (!result.second && g_ActiveConfig.bShaderCache)
AppendToPipelineUIDCache(m_pipeline_state);
// Still waiting for the pipeline to compile?
if (!result.first.second)
return result.first.first;
// Use ubershader instead.
m_using_ubershaders = true;
UpdatePipelineLayout();
UpdatePipelineVertexFormat();
PipelineInfo uber_info = m_pipeline_state;
UberShader::VertexShaderUid uber_vuid = UberShader::GetVertexShaderUid();
UberShader::PixelShaderUid uber_puid = UberShader::GetPixelShaderUid();
UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_puid);
uber_info.vs = g_shader_cache->GetVertexUberShaderForUid(uber_vuid);
uber_info.ps = g_shader_cache->GetPixelUberShaderForUid(uber_puid);
auto uber_result = g_shader_cache->GetPipelineWithCacheResult(uber_info);
return uber_result.first;
}
else
{
// Add to the UID cache if it is a new pipeline.
auto result = g_shader_cache->GetPipelineWithCacheResult(m_pipeline_state);
if (!result.second && !m_using_ubershaders && g_ActiveConfig.bShaderCache)
AppendToPipelineUIDCache(m_pipeline_state);
return result.first;
}
}
bool StateTracker::IsSSBODescriptorRequired() const
{
return m_bbox_enabled || (m_using_ubershaders && g_ActiveConfig.bBBoxEnable &&
g_ActiveConfig.BBoxUseFragmentShaderImplementation());
}
bool StateTracker::UpdatePipeline()
{
// We need at least a vertex and fragment shader
if (m_pipeline_state.vs == VK_NULL_HANDLE || m_pipeline_state.ps == VK_NULL_HANDLE)
return false;
// Grab a new pipeline object, this can fail.
m_pipeline_object = GetPipelineAndCacheUID();
m_dirty_flags |= DIRTY_FLAG_PIPELINE_BINDING;
return m_pipeline_object != VK_NULL_HANDLE;
}
void StateTracker::UpdatePipelineLayout()
{
const bool use_bbox_pipeline_layout = IsSSBODescriptorRequired();
VkPipelineLayout pipeline_layout =
use_bbox_pipeline_layout ? g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
if (m_pipeline_state.pipeline_layout == pipeline_layout)
return;
// Change the number of active descriptor sets, as well as the pipeline layout
m_pipeline_state.pipeline_layout = pipeline_layout;
if (use_bbox_pipeline_layout)
{
m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS;
// The bbox buffer never changes, so we defer descriptor updates until it is enabled.
if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
}
else
{
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
}
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
}
void StateTracker::UpdatePipelineVertexFormat()
{
const NativeVertexFormat* vertex_format =
m_using_ubershaders ?
VertexLoaderManager::GetUberVertexFormat(m_vertex_format->GetVertexDeclaration()) :
m_vertex_format;
if (m_pipeline_state.vertex_format == vertex_format)
return;
m_pipeline_state.vertex_format = static_cast<const VertexFormat*>(vertex_format);
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
}
bool StateTracker::UpdateDescriptorSet() bool StateTracker::UpdateDescriptorSet()
{ {
const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO 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++) 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, writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr, nullptr,
set, set,
@ -1091,7 +681,7 @@ bool StateTracker::UpdateDescriptorSet()
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING; m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
} }
if (IsSSBODescriptorRequired() && if (g_vulkan_context->SupportsBoundingBox() &&
(m_dirty_flags & DIRTY_FLAG_PS_SSBO || (m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)) 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) if (num_writes > 0)
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr); vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr);
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
return true; return true;
} }

View File

@ -9,19 +9,14 @@
#include <memory> #include <memory>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/LinearDiskCache.h"
#include "VideoBackends/Vulkan/Constants.h" #include "VideoBackends/Vulkan/Constants.h"
#include "VideoBackends/Vulkan/ShaderCache.h" #include "VideoBackends/Vulkan/ShaderCache.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/RenderBase.h" #include "VideoCommon/RenderBase.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexShaderGen.h"
namespace Vulkan namespace Vulkan
{ {
class VKPipeline;
class StreamBuffer; class StreamBuffer;
class VertexFormat; class VertexFormat;
@ -35,31 +30,18 @@ public:
static bool CreateInstance(); static bool CreateInstance();
static void DestroyInstance(); static void DestroyInstance();
const RasterizationState& GetRasterizationState() const
{
return m_pipeline_state.rasterization_state;
}
const DepthState& GetDepthStencilState() const { return m_pipeline_state.depth_state; }
const BlendingState& GetBlendState() const { return m_pipeline_state.blend_state; }
const std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS>& GetPSSamplerBindings() const const std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS>& GetPSSamplerBindings() const
{ {
return m_bindings.ps_samplers; return m_bindings.ps_samplers;
} }
VkFramebuffer GetFramebuffer() const { return m_framebuffer; } VkFramebuffer GetFramebuffer() const { return m_framebuffer; }
const VKPipeline* GetPipeline() const { return m_pipeline; }
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset); void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type); void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
void SetRenderPass(VkRenderPass load_render_pass, VkRenderPass clear_render_pass); void SetRenderPass(VkRenderPass load_render_pass, VkRenderPass clear_render_pass);
void SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& render_area); void SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& render_area);
void SetVertexFormat(const VertexFormat* vertex_format); void SetPipeline(const VKPipeline* pipeline);
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 UpdateVertexShaderConstants(); void UpdateVertexShaderConstants();
void UpdateGeometryShaderConstants(); void UpdateGeometryShaderConstants();
@ -68,7 +50,6 @@ public:
void SetTexture(size_t index, VkImageView view); void SetTexture(size_t index, VkImageView view);
void SetSampler(size_t index, VkSampler sampler); void SetSampler(size_t index, VkSampler sampler);
void SetBBoxEnable(bool enable);
void SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range); void SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range);
void UnbindTexture(VkImageView view); void UnbindTexture(VkImageView view);
@ -117,30 +98,11 @@ public:
bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const; 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: 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. // Number of descriptor sets for game draws.
enum enum
{ {
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1, NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
}; };
enum DITRY_FLAG : u32 enum DITRY_FLAG : u32
@ -157,7 +119,6 @@ private:
DIRTY_FLAG_SCISSOR = (1 << 9), DIRTY_FLAG_SCISSOR = (1 << 9),
DIRTY_FLAG_PIPELINE = (1 << 10), DIRTY_FLAG_PIPELINE = (1 << 10),
DIRTY_FLAG_DESCRIPTOR_SET_BINDING = (1 << 11), 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_ALL_DESCRIPTOR_SETS = DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO |
DIRTY_FLAG_PS_SAMPLERS | DIRTY_FLAG_PS_SSBO DIRTY_FLAG_PS_SAMPLERS | DIRTY_FLAG_PS_SSBO
@ -165,28 +126,10 @@ private:
bool Initialize(); 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. // Check that the specified viewport is within the render area.
// If not, ends the render pass if it is a clear render pass. // If not, ends the render pass if it is a clear render pass.
bool IsViewportWithinRenderArea() const; 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(); bool UpdateDescriptorSet();
// Allocates storage in the uniform buffer of the specified size. If this storage cannot be // 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; VkDeviceSize m_index_buffer_offset = 0;
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16; 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 // pipeline state
PipelineInfo m_pipeline_state = {}; const VKPipeline* m_pipeline = nullptr;
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
const VertexFormat* m_vertex_format = nullptr;
// shader bindings // shader bindings
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {}; std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {};
@ -230,8 +163,8 @@ private:
VkDescriptorBufferInfo ps_ssbo = {}; VkDescriptorBufferInfo ps_ssbo = {};
} m_bindings; } m_bindings;
u32 m_num_active_descriptor_sets = 0;
size_t m_uniform_buffer_reserve_size = 0; size_t m_uniform_buffer_reserve_size = 0;
u32 m_num_active_descriptor_sets = 0;
// rasterization // rasterization
VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; 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; VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
VkRect2D m_framebuffer_size = {}; VkRect2D m_framebuffer_size = {};
VkRect2D m_framebuffer_render_area = {}; VkRect2D m_framebuffer_render_area = {};
bool m_bbox_enabled = false;
// CPU access tracking // CPU access tracking
u32 m_draw_counter = 0; u32 m_draw_counter = 0;
std::vector<u32> m_cpu_accesses_this_frame; std::vector<u32> m_cpu_accesses_this_frame;
std::vector<u32> m_scheduled_command_buffer_kicks; std::vector<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true; bool m_allow_background_execution = true;
// Draw state cache on disk
// We don't actually use the value field here, instead we generate the shaders from the uid
// on-demand. If all goes well, it should hit the shader and Vulkan pipeline cache, therefore
// loading should be reasonably efficient.
LinearDiskCache<SerializedPipelineUID, u32> m_uid_cache;
}; };
} }

View File

@ -592,16 +592,19 @@ void UtilityShaderDraw::BindDescriptors()
&dummy_uniform_buffer, &dummy_uniform_buffer,
nullptr}; nullptr};
set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, if (g_vulkan_context->SupportsGeometryShaders())
nullptr, {
set, set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
UBO_DESCRIPTOR_SET_BINDING_GS, nullptr,
0, set,
1, UBO_DESCRIPTOR_SET_BINDING_GS,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 0,
nullptr, 1,
&dummy_uniform_buffer, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
nullptr}; nullptr,
&dummy_uniform_buffer,
nullptr};
}
set_writes[num_set_writes++] = { set_writes[num_set_writes++] = {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, UBO_DESCRIPTOR_SET_BINDING_PS, 0, 1, VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, UBO_DESCRIPTOR_SET_BINDING_PS, 0, 1,

View File

@ -14,7 +14,9 @@
namespace Vulkan 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> VKPipeline::Create(const AbstractPipelineConfig& con
// Get render pass for config. // Get render pass for config.
VkRenderPass render_pass = g_object_cache->GetRenderPass( VkRenderPass render_pass = g_object_cache->GetRenderPass(
Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.color_texture_format), 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. // Get pipeline layout.
VkPipelineLayout pipeline_layout; VkPipelineLayout pipeline_layout;
@ -68,6 +71,6 @@ std::unique_ptr<VKPipeline> VKPipeline::Create(const AbstractPipelineConfig& con
if (pipeline == VK_NULL_HANDLE) if (pipeline == VK_NULL_HANDLE)
return nullptr; return nullptr;
return std::make_unique<VKPipeline>(pipeline); return std::make_unique<VKPipeline>(pipeline, pipeline_layout, config.usage);
} }
} // namespace Vulkan } // namespace Vulkan

View File

@ -14,14 +14,19 @@ namespace Vulkan
class VKPipeline final : public AbstractPipeline class VKPipeline final : public AbstractPipeline
{ {
public: public:
explicit VKPipeline(VkPipeline pipeline); explicit VKPipeline(VkPipeline pipeline, VkPipelineLayout pipeline_layout,
AbstractPipelineUsage usage);
~VKPipeline() override; ~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<VKPipeline> Create(const AbstractPipelineConfig& config); static std::unique_ptr<VKPipeline> Create(const AbstractPipelineConfig& config);
private: private:
VkPipeline m_pipeline; VkPipeline m_pipeline;
VkPipelineLayout m_pipeline_layout;
AbstractPipelineUsage m_usage;
}; };
} // namespace Vulkan } // namespace Vulkan

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "Common/Align.h"
#include "Common/Assert.h" #include "Common/Assert.h"
#include "VideoBackends/Vulkan/ShaderCompiler.h" #include "VideoBackends/Vulkan/ShaderCompiler.h"
@ -103,20 +104,11 @@ std::unique_ptr<VKShader> VKShader::CreateFromSource(ShaderStage stage, const ch
std::unique_ptr<VKShader> VKShader::CreateFromBinary(ShaderStage stage, const void* data, std::unique_ptr<VKShader> VKShader::CreateFromBinary(ShaderStage stage, const void* data,
size_t length) size_t length)
{ {
ShaderCompiler::SPIRVCodeVector spv; const size_t size_in_words = Common::AlignUp(length, sizeof(ShaderCompiler::SPIRVCodeType)) /
const size_t size_in_words = sizeof(length) / sizeof(ShaderCompiler::SPIRVCodeType); sizeof(ShaderCompiler::SPIRVCodeType);
if (size_in_words > 0) ShaderCompiler::SPIRVCodeVector spv(size_in_words);
{ if (length > 0)
spv.resize(length / size_in_words); std::memcpy(spv.data(), data, length);
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)));
}
return CreateShaderObject(stage, std::move(spv)); return CreateShaderObject(stage, std::move(spv));
} }

View File

@ -139,8 +139,6 @@ void VertexManager::vFlush()
u32 index_count = IndexGenerator::GetIndexLen(); u32 index_count = IndexGenerator::GetIndexLen();
// Update tracked state // Update tracked state
StateTracker::GetInstance()->SetVertexFormat(vertex_format);
StateTracker::GetInstance()->CheckForShaderChanges();
StateTracker::GetInstance()->UpdateVertexShaderConstants(); StateTracker::GetInstance()->UpdateVertexShaderConstants();
StateTracker::GetInstance()->UpdateGeometryShaderConstants(); StateTracker::GetInstance()->UpdateGeometryShaderConstants();
StateTracker::GetInstance()->UpdatePixelShaderConstants(); StateTracker::GetInstance()->UpdatePixelShaderConstants();
@ -165,12 +163,10 @@ void VertexManager::vFlush()
bounding_box->Flush(); bounding_box->Flush();
bounding_box->Invalidate(); 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 // Bind all pending state to the command buffer
g_renderer->SetPipeline(m_current_pipeline_object);
if (!StateTracker::GetInstance()->Bind()) if (!StateTracker::GetInstance()->Bind())
{ {
WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count); WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count);

View File

@ -235,6 +235,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config)
config->backend_info.bSupportsBitfield = true; // Assumed support. config->backend_info.bSupportsBitfield = true; // Assumed support.
config->backend_info.bSupportsDynamicSamplerIndexing = true; // Assumed support. config->backend_info.bSupportsDynamicSamplerIndexing = true; // Assumed support.
config->backend_info.bSupportsPostProcessing = 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.bSupportsDualSourceBlend = false; // Dependent on features.
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features. config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
config->backend_info.bSupportsGSInstancing = false; // Dependent on features. config->backend_info.bSupportsGSInstancing = false; // Dependent on features.

View File

@ -223,6 +223,7 @@ bool VideoBackend::Initialize(void* window_handle)
g_renderer = std::make_unique<Renderer>(std::move(swap_chain)); g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
g_vertex_manager = std::make_unique<VertexManager>(); g_vertex_manager = std::make_unique<VertexManager>();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
::g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
g_perf_query = std::make_unique<PerfQuery>(); g_perf_query = std::make_unique<PerfQuery>();
// Invoke init methods on main wrapper classes. // 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. // for the remaining classes may call methods on these.
if (!StateTracker::CreateInstance() || !FramebufferManager::GetInstance()->Initialize() || if (!StateTracker::CreateInstance() || !FramebufferManager::GetInstance()->Initialize() ||
!Renderer::GetInstance()->Initialize() || !VertexManager::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."); PanicAlert("Failed to initialize Vulkan classes.");
Shutdown(); Shutdown();
return false; 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. // Display the name so the user knows which device was actually created.
INFO_LOG(VIDEO, "Vulkan Device: %s", g_vulkan_context->GetDeviceProperties().deviceName); INFO_LOG(VIDEO, "Vulkan Device: %s", g_vulkan_context->GetDeviceProperties().deviceName);
return true; return true;
@ -252,13 +246,17 @@ bool VideoBackend::Initialize(void* window_handle)
void VideoBackend::Shutdown() void VideoBackend::Shutdown()
{ {
if (g_renderer)
g_renderer->Shutdown();
if (g_command_buffer_mgr) if (g_command_buffer_mgr)
g_command_buffer_mgr->WaitForGPUIdle(); g_command_buffer_mgr->WaitForGPUIdle();
if (::g_shader_cache)
::g_shader_cache->Shutdown();
if (g_renderer)
g_renderer->Shutdown();
g_perf_query.reset(); g_perf_query.reset();
::g_shader_cache.reset();
g_texture_cache.reset(); g_texture_cache.reset();
g_vertex_manager.reset(); g_vertex_manager.reset();
g_renderer.reset(); g_renderer.reset();

View File

@ -18,7 +18,6 @@ AsyncShaderCompiler::~AsyncShaderCompiler()
// Pending work can be left at shutdown. // Pending work can be left at shutdown.
// The work item classes are expected to clean up after themselves. // The work item classes are expected to clean up after themselves.
_assert_(!HasWorkerThreads()); _assert_(!HasWorkerThreads());
_assert_(m_completed_work.empty());
} }
void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item) void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item)

View File

@ -28,9 +28,7 @@ void FlushPipeline()
void SetGenerationMode() void SetGenerationMode()
{ {
RasterizationState state = {}; g_vertex_manager->SetRasterizationStateChanged();
state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType());
g_renderer->SetRasterizationState(state);
} }
void SetScissor() void SetScissor()
@ -129,16 +127,12 @@ void SetViewport()
void SetDepthMode() void SetDepthMode()
{ {
DepthState state = {}; g_vertex_manager->SetDepthStateChanged();
state.Generate(bpmem);
g_renderer->SetDepthState(state);
} }
void SetBlendMode() void SetBlendMode()
{ {
BlendingState state = {}; g_vertex_manager->SetBlendingStateChanged();
state.Generate(bpmem);
g_renderer->SetBlendingState(state);
} }
/* Explanation of the magic behind ClearScreen: /* Explanation of the magic behind ClearScreen:

View File

@ -32,6 +32,7 @@ set(SRCS
PostProcessing.cpp PostProcessing.cpp
RenderBase.cpp RenderBase.cpp
RenderState.cpp RenderState.cpp
ShaderCache.cpp
ShaderGenCommon.cpp ShaderGenCommon.cpp
Statistics.cpp Statistics.cpp
UberShaderCommon.cpp UberShaderCommon.cpp

View File

@ -102,8 +102,6 @@ static BugInfo m_known_bugs[] = {
true}, true},
{API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN, {API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN,
BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true}, 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, {API_VULKAN, OS_ALL, VENDOR_NVIDIA, DRIVER_NVIDIA, Family::UNKNOWN, BUG_BROKEN_MSAA_CLEAR, -1.0,
-1.0, true}, -1.0, true},
{API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN, {API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN,

View File

@ -252,8 +252,10 @@ enum Bug
// the negated value to a temporary variable then using that in the bitwise op. // the negated value to a temporary variable then using that in the bitwise op.
BUG_BROKEN_BITWISE_OP_NEGATION, BUG_BROKEN_BITWISE_OP_NEGATION,
// Bug: Shaders are recompiled on the main thread after being previously compiled on // BUG: The GPU shader code appears to be context-specific on Mesa/i965.
// a worker thread 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 // Started version: -1
// Ended Version: -1 // Ended Version: -1
BUG_SHARED_CONTEXT_SHADER_COMPILATION, BUG_SHARED_CONTEXT_SHADER_COMPILATION,

View File

@ -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

View File

@ -8,7 +8,6 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
#include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/VertexManagerBase.h"
enum class APIType; enum class APIType;

View File

@ -56,6 +56,7 @@
#include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/PostProcessing.h" #include "VideoCommon/PostProcessing.h"
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/Statistics.h" #include "VideoCommon/Statistics.h"
#include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureCacheBase.h"
@ -92,6 +93,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
m_surface_handle = Host_GetRenderHandle(); m_surface_handle = Host_GetRenderHandle();
m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits;
m_last_efb_multisamples = g_ActiveConfig.iMultisamples;
} }
Renderer::~Renderer() = default; Renderer::~Renderer() = default;
@ -234,11 +236,20 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet
bool Renderer::CheckForHostConfigChanges() bool Renderer::CheckForHostConfigChanges()
{ {
ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); 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; return false;
}
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
m_last_host_config_bits = new_host_config.bits; 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; 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 // Set default viewport and scissor, for the clear to work correctly
// New frame // New frame
stats.ResetFrame(); 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); Core::Callback_VideoCopiedToXFB(true);
} }
@ -1009,3 +1027,8 @@ bool Renderer::UseVertexDepthRange() const
// in the vertex shader. // in the vertex shader.
return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f;
} }
std::unique_ptr<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCompiler()
{
return std::make_unique<VideoCommon::AsyncShaderCompiler>();
}

View File

@ -28,6 +28,7 @@
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
#include "VideoCommon/AVIDump.h" #include "VideoCommon/AVIDump.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/BPMemory.h" #include "VideoCommon/BPMemory.h"
#include "VideoCommon/FPSCounter.h" #include "VideoCommon/FPSCounter.h"
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
@ -78,10 +79,7 @@ public:
}; };
virtual void SetPipeline(const AbstractPipeline* pipeline) {} virtual void SetPipeline(const AbstractPipeline* pipeline) {}
virtual void SetBlendingState(const BlendingState& state) {}
virtual void SetScissorRect(const MathUtil::Rectangle<int>& rc) {} virtual void SetScissorRect(const MathUtil::Rectangle<int>& rc) {}
virtual void SetRasterizationState(const RasterizationState& state) {}
virtual void SetDepthState(const DepthState& state) {}
virtual void SetTexture(u32 index, const AbstractTexture* texture) {} virtual void SetTexture(u32 index, const AbstractTexture* texture) {}
virtual void SetSamplerState(u32 index, const SamplerState& state) {} virtual void SetSamplerState(u32 index, const SamplerState& state) {}
virtual void UnbindTexture(const AbstractTexture* texture) {} virtual void UnbindTexture(const AbstractTexture* texture) {}
@ -189,6 +187,8 @@ public:
void ResizeSurface(int new_width, int new_height); void ResizeSurface(int new_width, int new_height);
bool UseVertexDepthRange() const; bool UseVertexDepthRange() const;
virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler();
virtual void Shutdown(); virtual void Shutdown();
// Drawing utility shaders. // Drawing utility shaders.
@ -243,6 +243,7 @@ protected:
std::mutex m_swap_mutex; std::mutex m_swap_mutex;
u32 m_last_host_config_bits = 0; u32 m_last_host_config_bits = 0;
u32 m_last_efb_multisamples = 1;
private: private:
void RunFrameDumps(); void RunFrameDumps();

View File

@ -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<VideoCommon::ShaderCache> 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<AbstractPipeline> pipeline;
std::optional<AbstractPipelineConfig> 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<const AbstractPipeline*> 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<AbstractPipeline> pipeline;
std::optional<AbstractPipelineConfig> 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<int>(completed), static_cast<int>(total));
});
m_async_shader_compiler->RetrieveWorkItems();
}
Host_UpdateProgressDialog("", -1, -1);
}
template <ShaderStage stage, typename K, typename T>
static void LoadShaderCache(T& cache, APIType api_type, const char* type, bool include_gameid)
{
class CacheReader : public LinearDiskCacheReader<K, u8>
{
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 <typename T>
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<ShaderStage::Vertex, UberShader::VertexShaderUid>(m_uber_vs_cache, m_api_type,
"uber-vs", false);
LoadShaderCache<ShaderStage::Pixel, UberShader::PixelShaderUid>(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<ShaderStage::Geometry, GeometryShaderUid>(m_gs_cache, m_api_type, "gs", false);
// Specialized shaders, gameid-specific.
LoadShaderCache<ShaderStage::Vertex, VertexShaderUid>(m_vs_cache, m_api_type, "specialized-vs",
true);
LoadShaderCache<ShaderStage::Pixel, PixelShaderUid>(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<GXPipelineDiskCacheUid, u8>
{
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<AbstractShader> 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<AbstractShader>
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<AbstractShader> 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<AbstractShader>
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<AbstractShader> 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<u32>(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<AbstractShader> 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<u32>(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<AbstractShader> 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<u32>(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<AbstractShader> 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<u32>(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<AbstractShader> 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<u32>(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<AbstractPipelineConfig> 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<AbstractPipelineConfig>
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<AbstractPipeline> 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<AbstractPipeline> 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<AbstractShader> shader;
VertexShaderUid uid;
};
m_vs_cache.shader_map[uid].pending = true;
auto wi = m_async_shader_compiler->CreateWorkItem<VertexShaderWorkItem>(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<AbstractShader> shader;
UberShader::VertexShaderUid uid;
};
m_uber_vs_cache.shader_map[uid].pending = true;
auto wi = m_async_shader_compiler->CreateWorkItem<VertexUberShaderWorkItem>(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<AbstractShader> shader;
PixelShaderUid uid;
};
m_ps_cache.shader_map[uid].pending = true;
auto wi = m_async_shader_compiler->CreateWorkItem<PixelShaderWorkItem>(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<AbstractShader> shader;
UberShader::PixelShaderUid uid;
};
m_uber_ps_cache.shader_map[uid].pending = true;
auto wi = m_async_shader_compiler->CreateWorkItem<PixelUberShaderWorkItem>(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<AbstractPipeline> 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<PipelineWorkItem>(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<AbstractPipeline> 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<UberPipelineCompilePass>(
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<UberPipelinePreparePass>(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

View File

@ -0,0 +1,162 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstddef>
#include <cstring>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#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<const AbstractPipeline*> 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<AbstractShader> CompileVertexShader(const VertexShaderUid& uid) const;
std::unique_ptr<AbstractShader>
CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const;
std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid) const;
std::unique_ptr<AbstractShader>
CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const;
const AbstractShader* InsertVertexShader(const VertexShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertVertexUberShader(const UberShader::VertexShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertPixelShader(const PixelShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertPixelUberShader(const UberShader::PixelShaderUid& uid,
std::unique_ptr<AbstractShader> 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<AbstractPipelineConfig> GetGXPipelineConfig(const GXPipelineUid& uid);
std::optional<AbstractPipelineConfig> GetGXUberPipelineConfig(const GXUberPipelineUid& uid);
const AbstractPipeline* InsertGXPipeline(const GXPipelineUid& config,
std::unique_ptr<AbstractPipeline> pipeline);
const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineUid& config,
std::unique_ptr<AbstractPipeline> 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<AsyncShaderCompiler> m_async_shader_compiler;
// GX Shader Caches
template <typename Uid>
struct ShaderModuleCache
{
struct Shader
{
std::unique_ptr<AbstractShader> shader;
bool pending;
};
std::map<Uid, Shader> shader_map;
LinearDiskCache<Uid, u8> disk_cache;
};
ShaderModuleCache<VertexShaderUid> m_vs_cache;
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
ShaderModuleCache<PixelShaderUid> m_ps_cache;
ShaderModuleCache<UberShader::VertexShaderUid> m_uber_vs_cache;
ShaderModuleCache<UberShader::PixelShaderUid> m_uber_ps_cache;
// GX Pipeline Caches - .first - pipeline, .second - pending
std::map<GXPipelineUid, std::pair<std::unique_ptr<AbstractPipeline>, bool>> m_gx_pipeline_cache;
std::map<GXUberPipelineUid, std::pair<std::unique_ptr<AbstractPipeline>, 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<GXPipelineDiskCacheUid, u8> m_gx_pipeline_uid_disk_cache;
};
} // namespace VideoCommon
extern std::unique_ptr<VideoCommon::ShaderCache> g_shader_cache;

View File

@ -37,28 +37,31 @@ ShaderHostConfig ShaderHostConfig::GetCurrent()
} }
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid, 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))) if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
std::string filename = File::GetUserPath(D_SHADERCACHE_IDX); std::string filename = File::GetUserPath(D_SHADERCACHE_IDX);
switch (api_type) if (include_api)
{ {
case APIType::D3D: switch (api_type)
filename += "D3D"; {
break; case APIType::D3D:
case APIType::OpenGL: filename += "D3D";
filename += "OpenGL"; break;
break; case APIType::OpenGL:
case APIType::Vulkan: filename += "OpenGL";
filename += "Vulkan"; break;
break; case APIType::Vulkan:
default: filename += "Vulkan";
break; break;
default:
break;
}
filename += '-';
} }
filename += '-';
filename += type; filename += type;
if (include_gameid) if (include_gameid)

View File

@ -187,7 +187,7 @@ union ShaderHostConfig
// Gets the filename of the specified type of cache object (e.g. vertex shader, pipeline). // 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, 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 <class T> template <class T>
inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type, inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type,

View File

@ -105,10 +105,8 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count,
Flush(); Flush();
// Have to update the rasterization state for point/line cull modes. // 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; m_current_primitive_type = new_primitive_type;
SetRasterizationStateChanged();
} }
// Check for size in buffer, if the buffer gets full, call Flush() // Check for size in buffer, if the buffer gets full, call Flush()
@ -386,6 +384,10 @@ void VertexManagerBase::Flush()
if (!m_cull_all) if (!m_cull_all)
{ {
// Update the pipeline, or compile one if needed.
UpdatePipelineConfig();
UpdatePipelineObject();
// set the rest of the global constants // set the rest of the global constants
GeometryShaderManager::SetConstants(); GeometryShaderManager::SetConstants();
PixelShaderManager::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.f0 = out[2] - (out[0] * m_zslope.dfdx + out[1] * m_zslope.dfdy);
m_zslope.dirty = true; 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);
}

View File

@ -10,6 +10,7 @@
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
#include "VideoCommon/ShaderCache.h"
class DataReader; class DataReader;
class NativeVertexFormat; class NativeVertexFormat;
@ -58,6 +59,16 @@ public:
std::pair<size_t, size_t> ResetFlushAspectRatioCount(); std::pair<size_t, size_t> 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: protected:
virtual void vDoState(PointerWrap& p) {} virtual void vDoState(PointerWrap& p) {}
virtual void ResetBuffer(u32 stride) = 0; virtual void ResetBuffer(u32 stride) = 0;
@ -72,8 +83,15 @@ protected:
Slope m_zslope = {}; Slope m_zslope = {};
void CalculateZSlope(NativeVertexFormat* format); 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; 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: private:
bool m_is_flushed = true; bool m_is_flushed = true;
@ -84,6 +102,8 @@ private:
virtual void CreateDeviceObjects() {} virtual void CreateDeviceObjects() {}
virtual void DestroyDeviceObjects() {} virtual void DestroyDeviceObjects() {}
void UpdatePipelineConfig();
void UpdatePipelineObject();
}; };
extern std::unique_ptr<VertexManagerBase> g_vertex_manager; extern std::unique_ptr<VertexManagerBase> g_vertex_manager;

View File

@ -68,6 +68,7 @@
<ClCompile Include="RenderBase.cpp" /> <ClCompile Include="RenderBase.cpp" />
<ClCompile Include="RenderState.cpp" /> <ClCompile Include="RenderState.cpp" />
<ClCompile Include="LightingShaderGen.cpp" /> <ClCompile Include="LightingShaderGen.cpp" />
<ClCompile Include="ShaderCache.cpp" />
<ClCompile Include="ShaderGenCommon.cpp" /> <ClCompile Include="ShaderGenCommon.cpp" />
<ClCompile Include="UberShaderCommon.cpp" /> <ClCompile Include="UberShaderCommon.cpp" />
<ClCompile Include="UberShaderPixel.cpp" /> <ClCompile Include="UberShaderPixel.cpp" />
@ -119,6 +120,8 @@
<ClInclude Include="Fifo.h" /> <ClInclude Include="Fifo.h" />
<ClInclude Include="FPSCounter.h" /> <ClInclude Include="FPSCounter.h" />
<ClInclude Include="FramebufferManagerBase.h" /> <ClInclude Include="FramebufferManagerBase.h" />
<ClInclude Include="GXPipelineTypes.h" />
<ClInclude Include="ShaderCache.h" />
<ClInclude Include="UberShaderCommon.h" /> <ClInclude Include="UberShaderCommon.h" />
<ClInclude Include="UberShaderPixel.h" /> <ClInclude Include="UberShaderPixel.h" />
<ClInclude Include="HiresTextures.h" /> <ClInclude Include="HiresTextures.h" />

View File

@ -197,6 +197,9 @@
<ClCompile Include="AbstractFramebuffer.cpp"> <ClCompile Include="AbstractFramebuffer.cpp">
<Filter>Base</Filter> <Filter>Base</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="ShaderCache.cpp">
<Filter>Shader Generators</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="CommandProcessor.h" /> <ClInclude Include="CommandProcessor.h" />
@ -378,8 +381,14 @@
<ClInclude Include="AbstractFramebuffer.h"> <ClInclude Include="AbstractFramebuffer.h">
<Filter>Base</Filter> <Filter>Base</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GXPipelineTypes.h">
<Filter>Shader Generators</Filter>
</ClInclude>
<ClInclude Include="ShaderCache.h">
<Filter>Shader Generators</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="CMakeLists.txt" /> <Text Include="CMakeLists.txt" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -102,9 +102,8 @@ void VideoConfig::Refresh()
bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING); bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING);
iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL); iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL);
bShaderCache = Config::Get(Config::GFX_SHADER_CACHE); bShaderCache = Config::Get(Config::GFX_SHADER_CACHE);
bBackgroundShaderCompiling = Config::Get(Config::GFX_BACKGROUND_SHADER_COMPILING); bWaitForShadersBeforeStarting = Config::Get(Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING);
bDisableSpecializedShaders = Config::Get(Config::GFX_DISABLE_SPECIALIZED_SHADERS); iUberShaderMode = static_cast<UberShaderMode>(Config::Get(Config::GFX_UBERSHADER_MODE));
bPrecompileUberShaders = Config::Get(Config::GFX_PRECOMPILE_UBER_SHADERS);
iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS); iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS);
iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS); iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS);
@ -187,6 +186,9 @@ static u32 GetNumAutoShaderCompilerThreads()
u32 VideoConfig::GetShaderCompilerThreads() const u32 VideoConfig::GetShaderCompilerThreads() const
{ {
if (!backend_info.bSupportsBackgroundCompiling)
return 0;
if (iShaderCompilerThreads >= 0) if (iShaderCompilerThreads >= 0)
return static_cast<u32>(iShaderCompilerThreads); return static_cast<u32>(iShaderCompilerThreads);
else else
@ -195,20 +197,15 @@ u32 VideoConfig::GetShaderCompilerThreads() const
u32 VideoConfig::GetShaderPrecompilerThreads() 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) if (iShaderPrecompilerThreads >= 0)
return static_cast<u32>(iShaderPrecompilerThreads); return static_cast<u32>(iShaderPrecompilerThreads);
else else
return GetNumAutoShaderCompilerThreads(); 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;
}

View File

@ -42,6 +42,13 @@ enum class StereoMode : int
Nvidia3DVision Nvidia3DVision
}; };
enum class UberShaderMode : int
{
Disabled,
Hybrid,
Exclusive
};
struct ProjectionHackConfig final struct ProjectionHackConfig final
{ {
bool m_enable; bool m_enable;
@ -161,25 +168,9 @@ struct VideoConfig final
// Currently only supported with Vulkan. // Currently only supported with Vulkan.
int iCommandBufferExecuteInterval; int iCommandBufferExecuteInterval;
// The following options determine the ubershader mode: // Shader compilation settings.
// No ubershaders: bool bWaitForShadersBeforeStarting;
// - bBackgroundShaderCompiling = false UberShaderMode iUberShaderMode;
// - 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;
// Number of shader compiler threads. // Number of shader compiler threads.
// 0 disables background compilation. // 0 disables background compilation.
@ -227,6 +218,7 @@ struct VideoConfig final
bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon
bool bSupportsBPTCTextures; bool bSupportsBPTCTextures;
bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES
bool bSupportsBackgroundCompiling;
} backend_info; } backend_info;
// Utility // Utility
@ -246,10 +238,9 @@ struct VideoConfig final
return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding; return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding;
} }
bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; } bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; }
bool UsingUberShaders() const { return iUberShaderMode != UberShaderMode::Disabled; }
u32 GetShaderCompilerThreads() const; u32 GetShaderCompilerThreads() const;
u32 GetShaderPrecompilerThreads() const; u32 GetShaderPrecompilerThreads() const;
bool CanPrecompileUberShaders() const;
bool CanBackgroundCompileShaders() const;
}; };
extern VideoConfig g_Config; extern VideoConfig g_Config;