Move shader caches to VideoCommon
This commit is contained in:
parent
24df896eb8
commit
dec0c3bce8
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
|
@ -25,16 +24,9 @@
|
|||
|
||||
namespace DX11
|
||||
{
|
||||
GeometryShaderCache::GSCache GeometryShaderCache::GeometryShaders;
|
||||
const GeometryShaderCache::GSCacheEntry* GeometryShaderCache::last_entry;
|
||||
GeometryShaderUid GeometryShaderCache::last_uid;
|
||||
const GeometryShaderCache::GSCacheEntry GeometryShaderCache::pass_entry;
|
||||
|
||||
ID3D11GeometryShader* ClearGeometryShader = nullptr;
|
||||
ID3D11GeometryShader* CopyGeometryShader = nullptr;
|
||||
|
||||
LinearDiskCache<GeometryShaderUid, u8> g_gs_disk_cache;
|
||||
|
||||
ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader()
|
||||
{
|
||||
return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? ClearGeometryShader : nullptr;
|
||||
|
@ -63,16 +55,6 @@ ID3D11Buffer*& GeometryShaderCache::GetConstantBuffer()
|
|||
return gscbuf;
|
||||
}
|
||||
|
||||
// this class will load the precompiled shaders into our cache
|
||||
class GeometryShaderCacheInserter : public LinearDiskCacheReader<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[] = {
|
||||
"struct VSOUTPUT\n"
|
||||
"{\n"
|
||||
|
@ -155,44 +137,6 @@ void GeometryShaderCache::Init()
|
|||
CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code);
|
||||
CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader");
|
||||
D3D::SetDebugObjectName(CopyGeometryShader, "copy geometry shader");
|
||||
|
||||
Clear();
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
LoadShaderCache();
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileShaders();
|
||||
}
|
||||
|
||||
void GeometryShaderCache::LoadShaderCache()
|
||||
{
|
||||
GeometryShaderCacheInserter inserter;
|
||||
g_gs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "GS", true, true), inserter);
|
||||
}
|
||||
|
||||
void GeometryShaderCache::Reload()
|
||||
{
|
||||
g_gs_disk_cache.Sync();
|
||||
g_gs_disk_cache.Close();
|
||||
Clear();
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
LoadShaderCache();
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileShaders();
|
||||
}
|
||||
|
||||
// ONLY to be used during shutdown.
|
||||
void GeometryShaderCache::Clear()
|
||||
{
|
||||
for (auto& iter : GeometryShaders)
|
||||
iter.second.Destroy();
|
||||
GeometryShaders.clear();
|
||||
|
||||
last_entry = nullptr;
|
||||
last_uid = {};
|
||||
}
|
||||
|
||||
void GeometryShaderCache::Shutdown()
|
||||
|
@ -201,83 +145,5 @@ void GeometryShaderCache::Shutdown()
|
|||
|
||||
SAFE_RELEASE(ClearGeometryShader);
|
||||
SAFE_RELEASE(CopyGeometryShader);
|
||||
|
||||
Clear();
|
||||
g_gs_disk_cache.Sync();
|
||||
g_gs_disk_cache.Close();
|
||||
}
|
||||
|
||||
bool GeometryShaderCache::SetShader(PrimitiveType primitive_type)
|
||||
{
|
||||
GeometryShaderUid uid = GetGeometryShaderUid(primitive_type);
|
||||
if (last_entry && uid == last_uid)
|
||||
{
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
|
||||
D3D::stateman->SetGeometryShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the shader is a pass-through shader
|
||||
if (uid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
// Return the default pass-through shader
|
||||
last_uid = uid;
|
||||
last_entry = &pass_entry;
|
||||
D3D::stateman->SetGeometryShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the shader is already in the cache
|
||||
auto iter = GeometryShaders.find(uid);
|
||||
if (iter != GeometryShaders.end())
|
||||
{
|
||||
const GSCacheEntry& entry = iter->second;
|
||||
last_uid = uid;
|
||||
last_entry = &entry;
|
||||
D3D::stateman->SetGeometryShader(last_entry->shader);
|
||||
return (entry.shader != nullptr);
|
||||
}
|
||||
|
||||
// Need to compile a new shader
|
||||
if (CompileShader(uid))
|
||||
return SetShader(primitive_type);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeometryShaderCache::CompileShader(const GeometryShaderUid& uid)
|
||||
{
|
||||
D3DBlob* bytecode;
|
||||
ShaderCode code =
|
||||
GenerateGeometryShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (!D3D::CompileGeometryShader(code.GetBuffer(), &bytecode) ||
|
||||
!InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0))
|
||||
{
|
||||
SAFE_RELEASE(bytecode);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert the bytecode into the caches
|
||||
g_gs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeometryShaderCache::InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode,
|
||||
size_t len)
|
||||
{
|
||||
GSCacheEntry& newentry = GeometryShaders[uid];
|
||||
newentry.shader = bytecode ? D3D::CreateGeometryShaderFromByteCode(bytecode, len) : nullptr;
|
||||
return newentry.shader != nullptr;
|
||||
}
|
||||
|
||||
void GeometryShaderCache::PrecompileShaders()
|
||||
{
|
||||
EnumerateGeometryShaderUids([](const GeometryShaderUid& uid) {
|
||||
if (GeometryShaders.find(uid) != GeometryShaders.end())
|
||||
return;
|
||||
|
||||
CompileShader(uid);
|
||||
});
|
||||
}
|
||||
|
||||
} // DX11
|
||||
|
|
|
@ -15,36 +15,12 @@ class GeometryShaderCache
|
|||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void Reload();
|
||||
static void Clear();
|
||||
static void Shutdown();
|
||||
static bool SetShader(PrimitiveType primitive_type);
|
||||
static bool CompileShader(const GeometryShaderUid& uid);
|
||||
static bool InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode, size_t len);
|
||||
static void PrecompileShaders();
|
||||
|
||||
static ID3D11GeometryShader* GetClearGeometryShader();
|
||||
static ID3D11GeometryShader* GetCopyGeometryShader();
|
||||
|
||||
static ID3D11Buffer*& GetConstantBuffer();
|
||||
|
||||
private:
|
||||
struct GSCacheEntry
|
||||
{
|
||||
ID3D11GeometryShader* shader;
|
||||
|
||||
GSCacheEntry() : shader(nullptr) {}
|
||||
void Destroy() { SAFE_RELEASE(shader); }
|
||||
};
|
||||
|
||||
typedef std::map<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
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "Common/Align.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
|
@ -27,17 +26,6 @@
|
|||
|
||||
namespace DX11
|
||||
{
|
||||
PixelShaderCache::PSCache PixelShaderCache::PixelShaders;
|
||||
PixelShaderCache::UberPSCache PixelShaderCache::UberPixelShaders;
|
||||
const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry;
|
||||
const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_uber_entry;
|
||||
PixelShaderUid PixelShaderCache::last_uid;
|
||||
UberShader::PixelShaderUid PixelShaderCache::last_uber_uid;
|
||||
|
||||
LinearDiskCache<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_ClearProgram = nullptr;
|
||||
ID3D11PixelShader* s_AnaglyphProgram = nullptr;
|
||||
|
@ -309,17 +297,6 @@ ID3D11Buffer* PixelShaderCache::GetConstantBuffer()
|
|||
return pscbuf;
|
||||
}
|
||||
|
||||
// this class will load the precompiled shaders into our cache
|
||||
template <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()
|
||||
{
|
||||
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);
|
||||
CHECK(s_ColorCopyProgram[0] != nullptr, "Create color copy pixel shader");
|
||||
D3D::SetDebugObjectName(s_ColorCopyProgram[0], "color copy pixel shader");
|
||||
|
||||
Clear();
|
||||
|
||||
SETSTAT(stats.numPixelShadersCreated, 0);
|
||||
SETSTAT(stats.numPixelShadersAlive, 0);
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
LoadShaderCache();
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
QueueUberShaderCompiles();
|
||||
}
|
||||
|
||||
void PixelShaderCache::LoadShaderCache()
|
||||
{
|
||||
PixelShaderCacheInserter<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
|
||||
|
@ -420,255 +345,5 @@ void PixelShaderCache::Shutdown()
|
|||
SAFE_RELEASE(s_rgba6_to_rgb8[i]);
|
||||
SAFE_RELEASE(s_rgb8_to_rgba6[i]);
|
||||
}
|
||||
|
||||
Clear();
|
||||
g_ps_disk_cache.Sync();
|
||||
g_ps_disk_cache.Close();
|
||||
g_uber_ps_disk_cache.Sync();
|
||||
g_uber_ps_disk_cache.Close();
|
||||
}
|
||||
|
||||
bool PixelShaderCache::SetShader()
|
||||
{
|
||||
if (g_ActiveConfig.bDisableSpecializedShaders)
|
||||
return SetUberShader();
|
||||
|
||||
PixelShaderUid uid = GetPixelShaderUid();
|
||||
ClearUnusedPixelShaderUidBits(APIType::D3D, &uid);
|
||||
if (last_entry && uid == last_uid)
|
||||
{
|
||||
if (last_entry->pending)
|
||||
return SetUberShader();
|
||||
|
||||
if (!last_entry->shader)
|
||||
return false;
|
||||
|
||||
D3D::stateman->SetPixelShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the shader is already in the cache
|
||||
auto iter = PixelShaders.find(uid);
|
||||
if (iter != PixelShaders.end())
|
||||
{
|
||||
const PSCacheEntry& entry = iter->second;
|
||||
if (entry.pending)
|
||||
return SetUberShader();
|
||||
|
||||
last_uid = uid;
|
||||
last_entry = &entry;
|
||||
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
|
||||
if (!last_entry->shader)
|
||||
return false;
|
||||
|
||||
D3D::stateman->SetPixelShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Background compiling?
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders())
|
||||
{
|
||||
// Create a pending entry
|
||||
PSCacheEntry entry;
|
||||
entry.pending = true;
|
||||
PixelShaders[uid] = entry;
|
||||
|
||||
// Queue normal shader compiling and use ubershader
|
||||
g_async_compiler->QueueWorkItem(
|
||||
g_async_compiler->CreateWorkItem<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
|
||||
|
|
|
@ -19,16 +19,7 @@ class PixelShaderCache
|
|||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void Reload();
|
||||
static void Clear();
|
||||
static void Shutdown();
|
||||
static bool SetShader();
|
||||
static bool SetUberShader();
|
||||
static bool InsertByteCode(const PixelShaderUid& uid, const u8* data, size_t len);
|
||||
static bool InsertByteCode(const UberShader::PixelShaderUid& uid, const u8* data, size_t len);
|
||||
static bool InsertShader(const PixelShaderUid& uid, ID3D11PixelShader* shader);
|
||||
static bool InsertShader(const UberShader::PixelShaderUid& uid, ID3D11PixelShader* shader);
|
||||
static void QueueUberShaderCompiles();
|
||||
|
||||
static ID3D11Buffer* GetConstantBuffer();
|
||||
|
||||
|
@ -40,58 +31,6 @@ public:
|
|||
static ID3D11PixelShader* ReinterpRGB8ToRGBA6(bool multisampled);
|
||||
|
||||
static void InvalidateMSAAShaders();
|
||||
|
||||
private:
|
||||
struct PSCacheEntry
|
||||
{
|
||||
ID3D11PixelShader* shader;
|
||||
bool pending;
|
||||
|
||||
PSCacheEntry() : shader(nullptr), pending(false) {}
|
||||
void Destroy() { SAFE_RELEASE(shader); }
|
||||
};
|
||||
|
||||
class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid);
|
||||
~PixelShaderCompilerWorkItem() override;
|
||||
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
PixelShaderUid m_uid;
|
||||
ID3D11PixelShader* m_shader = nullptr;
|
||||
D3DBlob* m_bytecode = nullptr;
|
||||
};
|
||||
|
||||
class UberPixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit UberPixelShaderCompilerWorkItem(const UberShader::PixelShaderUid& uid);
|
||||
~UberPixelShaderCompilerWorkItem() override;
|
||||
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
UberShader::PixelShaderUid m_uid;
|
||||
ID3D11PixelShader* m_shader = nullptr;
|
||||
D3DBlob* m_bytecode = nullptr;
|
||||
};
|
||||
|
||||
typedef std::map<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
|
||||
|
|
|
@ -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);
|
||||
SetupDeviceObjects();
|
||||
|
||||
// Setup GX pipeline state
|
||||
for (auto& sampler : m_gx_state.samplers)
|
||||
sampler.hex = RenderState::GetPointSamplerState().hex;
|
||||
|
||||
m_gx_state.zmode.testenable = false;
|
||||
m_gx_state.zmode.updateenable = false;
|
||||
m_gx_state.zmode.func = ZMode::NEVER;
|
||||
m_gx_state.raster.cullmode = GenMode::CULL_NONE;
|
||||
|
||||
// Clear EFB textures
|
||||
constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
|
||||
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
||||
|
@ -315,11 +306,6 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline)
|
|||
void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
|
||||
u32 vertex_stride, u32 num_vertices)
|
||||
{
|
||||
// Textures are fine, they're set directly via SetTexture.
|
||||
// Since samplers are set via gx_state, we need to fix this up here.
|
||||
for (size_t stage = 0; stage < m_gx_state.samplers.size(); stage++)
|
||||
D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage]));
|
||||
|
||||
// Copy in uniforms.
|
||||
if (uniforms_size > 0)
|
||||
{
|
||||
|
@ -640,11 +626,6 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
|
|||
RestoreAPIState();
|
||||
}
|
||||
|
||||
void Renderer::SetBlendingState(const BlendingState& state)
|
||||
{
|
||||
m_gx_state.blend.hex = state.hex;
|
||||
}
|
||||
|
||||
// This function has the final picture. We adjust the aspect ratio here.
|
||||
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks,
|
||||
float Gamma)
|
||||
|
@ -685,7 +666,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
// Enable configuration changes
|
||||
UpdateActiveConfig();
|
||||
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
||||
VertexShaderCache::RetreiveAsyncShaders();
|
||||
|
||||
// Flip/present backbuffer to frontbuffer here
|
||||
if (D3D::swapchain)
|
||||
|
@ -708,12 +688,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
D3D11_CLEAR_DEPTH, 0.f, 0);
|
||||
}
|
||||
|
||||
if (CheckForHostConfigChanges())
|
||||
{
|
||||
VertexShaderCache::Reload();
|
||||
GeometryShaderCache::Reload();
|
||||
PixelShaderCache::Reload();
|
||||
}
|
||||
CheckForHostConfigChanges();
|
||||
|
||||
// begin next frame
|
||||
RestoreAPIState();
|
||||
|
@ -786,30 +761,6 @@ void Renderer::RestoreAPIState()
|
|||
BPFunctions::SetScissor();
|
||||
}
|
||||
|
||||
void Renderer::ApplyState()
|
||||
{
|
||||
D3D::stateman->SetBlendState(m_state_cache.Get(m_gx_state.blend));
|
||||
D3D::stateman->SetDepthState(m_state_cache.Get(m_gx_state.zmode));
|
||||
D3D::stateman->SetRasterizerState(m_state_cache.Get(m_gx_state.raster));
|
||||
D3D::stateman->SetPrimitiveTopology(
|
||||
StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive));
|
||||
FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable);
|
||||
|
||||
for (u32 stage = 0; stage < static_cast<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)
|
||||
{
|
||||
const DXFramebuffer* fb = static_cast<const DXFramebuffer*>(framebuffer);
|
||||
|
@ -840,16 +791,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
m_gx_state.raster.hex = state.hex;
|
||||
}
|
||||
|
||||
void Renderer::SetDepthState(const DepthState& state)
|
||||
{
|
||||
m_gx_state.zmode.hex = state.hex;
|
||||
}
|
||||
|
||||
void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
|
||||
{
|
||||
D3D::stateman->SetTexture(
|
||||
|
@ -859,7 +800,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
|
|||
|
||||
void Renderer::SetSamplerState(u32 index, const SamplerState& state)
|
||||
{
|
||||
m_gx_state.samplers[index].hex = state.hex;
|
||||
D3D::stateman->SetSampler(index, m_state_cache.Get(state));
|
||||
}
|
||||
|
||||
void Renderer::UnbindTexture(const AbstractTexture* texture)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <string>
|
||||
#include "VideoBackends/D3D/D3DState.h"
|
||||
|
@ -41,10 +40,7 @@ public:
|
|||
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value = {},
|
||||
float depth_value = 0.0f) override;
|
||||
void SetBlendingState(const BlendingState& state) override;
|
||||
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
|
||||
void SetRasterizationState(const RasterizationState& state) override;
|
||||
void SetDepthState(const DepthState& state) override;
|
||||
void SetTexture(u32 index, const AbstractTexture* texture) override;
|
||||
void SetSamplerState(u32 index, const SamplerState& state) override;
|
||||
void UnbindTexture(const AbstractTexture* texture) override;
|
||||
|
@ -54,10 +50,6 @@ public:
|
|||
void SetFullscreen(bool enable_fullscreen) override;
|
||||
bool IsFullscreen() const override;
|
||||
|
||||
// TODO: Fix confusing names (see ResetAPIState and RestoreAPIState)
|
||||
void ApplyState() override;
|
||||
void RestoreState() override;
|
||||
|
||||
void RenderText(const std::string& text, int left, int top, u32 color) override;
|
||||
|
||||
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
|
||||
|
@ -84,14 +76,6 @@ public:
|
|||
u32 groups_x, u32 groups_y, u32 groups_z) override;
|
||||
|
||||
private:
|
||||
struct GXPipelineState
|
||||
{
|
||||
std::array<SamplerState, 8> samplers;
|
||||
BlendingState blend;
|
||||
DepthState zmode;
|
||||
RasterizationState raster;
|
||||
};
|
||||
|
||||
void SetupDeviceObjects();
|
||||
void TeardownDeviceObjects();
|
||||
void Create3DVisionTexture(int width, int height);
|
||||
|
@ -106,7 +90,6 @@ private:
|
|||
void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices);
|
||||
|
||||
StateCache m_state_cache;
|
||||
GXPipelineState m_gx_state;
|
||||
|
||||
std::array<ID3D11BlendState*, 4> m_clear_blend_states{};
|
||||
std::array<ID3D11DepthStencilState*, 3> m_clear_depth_states{};
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "VideoBackends/D3D/BoundingBox.h"
|
||||
#include "VideoBackends/D3D/D3DBase.h"
|
||||
#include "VideoBackends/D3D/D3DState.h"
|
||||
#include "VideoBackends/D3D/FramebufferManager.h"
|
||||
#include "VideoBackends/D3D/GeometryShaderCache.h"
|
||||
#include "VideoBackends/D3D/PixelShaderCache.h"
|
||||
#include "VideoBackends/D3D/Render.h"
|
||||
|
@ -135,42 +136,23 @@ void VertexManager::Draw(u32 stride)
|
|||
|
||||
void VertexManager::vFlush()
|
||||
{
|
||||
if (!PixelShaderCache::SetShader())
|
||||
{
|
||||
GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); });
|
||||
return;
|
||||
}
|
||||
|
||||
D3DVertexFormat* vertex_format =
|
||||
static_cast<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();
|
||||
|
||||
PrepareDrawBuffers(stride);
|
||||
|
||||
g_renderer->ApplyState();
|
||||
if (!m_current_pipeline_object)
|
||||
return;
|
||||
|
||||
FramebufferManager::SetIntegerEFBRenderTarget(
|
||||
m_current_pipeline_config.blending_state.logicopenable);
|
||||
g_renderer->SetPipeline(m_current_pipeline_object);
|
||||
|
||||
ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer();
|
||||
D3D::stateman->SetPixelConstants(PixelShaderCache::GetConstantBuffer(),
|
||||
g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr);
|
||||
D3D::stateman->SetVertexConstants(vertexConstants);
|
||||
D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer());
|
||||
|
||||
Draw(stride);
|
||||
|
||||
g_renderer->RestoreState();
|
||||
}
|
||||
|
||||
void VertexManager::ResetBuffer(u32 stride)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "Common/Align.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
|
@ -28,22 +27,11 @@
|
|||
|
||||
namespace DX11
|
||||
{
|
||||
VertexShaderCache::VSCache VertexShaderCache::vshaders;
|
||||
VertexShaderCache::UberVSCache VertexShaderCache::ubervshaders;
|
||||
const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry;
|
||||
const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_uber_entry;
|
||||
VertexShaderUid VertexShaderCache::last_uid;
|
||||
UberShader::VertexShaderUid VertexShaderCache::last_uber_uid;
|
||||
|
||||
static ID3D11VertexShader* SimpleVertexShader = nullptr;
|
||||
static ID3D11VertexShader* ClearVertexShader = nullptr;
|
||||
static ID3D11InputLayout* SimpleLayout = nullptr;
|
||||
static ID3D11InputLayout* ClearLayout = nullptr;
|
||||
|
||||
LinearDiskCache<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()
|
||||
{
|
||||
return SimpleVertexShader;
|
||||
|
@ -164,73 +152,12 @@ void VertexShaderCache::Init()
|
|||
D3D::SetDebugObjectName(ClearVertexShader, "clear vertex shader");
|
||||
D3D::SetDebugObjectName(ClearLayout, "clear input layout");
|
||||
|
||||
Clear();
|
||||
|
||||
SETSTAT(stats.numVertexShadersCreated, 0);
|
||||
SETSTAT(stats.numVertexShadersAlive, 0);
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
LoadShaderCache();
|
||||
|
||||
g_async_compiler = std::make_unique<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()
|
||||
{
|
||||
g_async_compiler->StopWorkerThreads();
|
||||
g_async_compiler->RetrieveWorkItems();
|
||||
|
||||
SAFE_RELEASE(vscbuf);
|
||||
|
||||
SAFE_RELEASE(SimpleVertexShader);
|
||||
|
@ -238,271 +165,5 @@ void VertexShaderCache::Shutdown()
|
|||
|
||||
SAFE_RELEASE(SimpleLayout);
|
||||
SAFE_RELEASE(ClearLayout);
|
||||
|
||||
Clear();
|
||||
g_vs_disk_cache.Sync();
|
||||
g_vs_disk_cache.Close();
|
||||
g_uber_vs_disk_cache.Sync();
|
||||
g_uber_vs_disk_cache.Close();
|
||||
}
|
||||
|
||||
bool VertexShaderCache::SetShader(D3DVertexFormat* vertex_format)
|
||||
{
|
||||
if (g_ActiveConfig.bDisableSpecializedShaders)
|
||||
return SetUberShader(vertex_format);
|
||||
|
||||
VertexShaderUid uid = GetVertexShaderUid();
|
||||
if (last_entry && uid == last_uid)
|
||||
{
|
||||
if (last_entry->pending)
|
||||
return SetUberShader(vertex_format);
|
||||
|
||||
if (!last_entry->shader)
|
||||
return false;
|
||||
|
||||
D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode));
|
||||
D3D::stateman->SetVertexShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto iter = vshaders.find(uid);
|
||||
if (iter != vshaders.end())
|
||||
{
|
||||
const VSCacheEntry& entry = iter->second;
|
||||
if (entry.pending)
|
||||
return SetUberShader(vertex_format);
|
||||
|
||||
last_uid = uid;
|
||||
last_entry = &entry;
|
||||
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true);
|
||||
if (!last_entry->shader)
|
||||
return false;
|
||||
|
||||
D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(last_entry->bytecode));
|
||||
D3D::stateman->SetVertexShader(last_entry->shader);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Background compiling?
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders())
|
||||
{
|
||||
// Create a pending entry
|
||||
VSCacheEntry entry;
|
||||
entry.pending = true;
|
||||
vshaders[uid] = entry;
|
||||
|
||||
// Queue normal shader compiling and use ubershader
|
||||
g_async_compiler->QueueWorkItem(
|
||||
g_async_compiler->CreateWorkItem<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
|
||||
|
|
|
@ -21,14 +21,7 @@ class VertexShaderCache
|
|||
{
|
||||
public:
|
||||
static void Init();
|
||||
static void Reload();
|
||||
static void Clear();
|
||||
static void Shutdown();
|
||||
static bool SetShader(D3DVertexFormat* vertex_format);
|
||||
static bool SetUberShader(D3DVertexFormat* vertex_format);
|
||||
static void RetreiveAsyncShaders();
|
||||
static void QueueUberShaderCompiles();
|
||||
static void WaitForBackgroundCompilesToComplete();
|
||||
|
||||
static ID3D11Buffer*& GetConstantBuffer();
|
||||
|
||||
|
@ -36,76 +29,6 @@ public:
|
|||
static ID3D11VertexShader* GetClearVertexShader();
|
||||
static ID3D11InputLayout* GetSimpleInputLayout();
|
||||
static ID3D11InputLayout* GetClearInputLayout();
|
||||
|
||||
static bool InsertByteCode(const VertexShaderUid& uid, D3DBlob* blob);
|
||||
static bool InsertByteCode(const UberShader::VertexShaderUid& uid, D3DBlob* blob);
|
||||
static bool InsertShader(const VertexShaderUid& uid, ID3D11VertexShader* shader, D3DBlob* blob);
|
||||
static bool InsertShader(const UberShader::VertexShaderUid& uid, ID3D11VertexShader* shader,
|
||||
D3DBlob* blob);
|
||||
|
||||
private:
|
||||
struct VSCacheEntry
|
||||
{
|
||||
ID3D11VertexShader* shader;
|
||||
D3DBlob* bytecode; // needed to initialize the input layout
|
||||
bool pending;
|
||||
|
||||
VSCacheEntry() : shader(nullptr), bytecode(nullptr), pending(false) {}
|
||||
void SetByteCode(D3DBlob* blob)
|
||||
{
|
||||
SAFE_RELEASE(bytecode);
|
||||
bytecode = blob;
|
||||
blob->AddRef();
|
||||
}
|
||||
void Destroy()
|
||||
{
|
||||
SAFE_RELEASE(shader);
|
||||
SAFE_RELEASE(bytecode);
|
||||
}
|
||||
};
|
||||
|
||||
class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid);
|
||||
~VertexShaderCompilerWorkItem() override;
|
||||
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
VertexShaderUid m_uid;
|
||||
D3DBlob* m_bytecode = nullptr;
|
||||
ID3D11VertexShader* m_vs = nullptr;
|
||||
};
|
||||
|
||||
class UberVertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit UberVertexShaderCompilerWorkItem(const UberShader::VertexShaderUid& uid);
|
||||
~UberVertexShaderCompilerWorkItem() override;
|
||||
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
UberShader::VertexShaderUid m_uid;
|
||||
D3DBlob* m_bytecode = nullptr;
|
||||
ID3D11VertexShader* m_vs = nullptr;
|
||||
};
|
||||
|
||||
typedef std::map<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
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "VideoBackends/D3D/VertexShaderCache.h"
|
||||
#include "VideoBackends/D3D/VideoBackend.h"
|
||||
|
||||
#include "VideoCommon/ShaderCache.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
|
@ -148,6 +149,7 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
|
||||
// internal interfaces
|
||||
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_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
|
@ -155,7 +157,9 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
VertexShaderCache::Init();
|
||||
PixelShaderCache::Init();
|
||||
GeometryShaderCache::Init();
|
||||
VertexShaderCache::WaitForBackgroundCompilesToComplete();
|
||||
if (!g_shader_cache->Initialize())
|
||||
return false;
|
||||
|
||||
D3D::InitUtils();
|
||||
BBox::Init();
|
||||
return true;
|
||||
|
@ -163,6 +167,7 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
|
||||
void VideoBackend::Shutdown()
|
||||
{
|
||||
g_shader_cache->Shutdown();
|
||||
g_renderer->Shutdown();
|
||||
|
||||
D3D::ShutdownUtils();
|
||||
|
@ -174,6 +179,7 @@ void VideoBackend::Shutdown()
|
|||
g_perf_query.reset();
|
||||
g_vertex_manager.reset();
|
||||
g_texture_cache.reset();
|
||||
g_shader_cache.reset();
|
||||
g_renderer.reset();
|
||||
|
||||
ShutdownShared();
|
||||
|
|
|
@ -3,7 +3,6 @@ set(SRCS
|
|||
NullTexture.cpp
|
||||
Render.cpp
|
||||
VertexManager.cpp
|
||||
ShaderCache.cpp
|
||||
)
|
||||
|
||||
set(LIBS
|
||||
|
|
|
@ -39,14 +39,12 @@
|
|||
<ClCompile Include="NullBackend.cpp" />
|
||||
<ClCompile Include="NullTexture.cpp" />
|
||||
<ClCompile Include="Render.cpp" />
|
||||
<ClCompile Include="ShaderCache.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="NullTexture.h" />
|
||||
<ClInclude Include="PerfQuery.h" />
|
||||
<ClInclude Include="Render.h" />
|
||||
<ClInclude Include="ShaderCache.h" />
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="VertexManager.h" />
|
||||
<ClInclude Include="VideoBackend.h" />
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "VideoBackends/Null/PerfQuery.h"
|
||||
#include "VideoBackends/Null/Render.h"
|
||||
#include "VideoBackends/Null/ShaderCache.h"
|
||||
#include "VideoBackends/Null/TextureCache.h"
|
||||
#include "VideoBackends/Null/VertexManager.h"
|
||||
#include "VideoBackends/Null/VideoBackend.h"
|
||||
|
@ -63,21 +62,15 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManagerBase>();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
|
||||
VertexShaderCache::s_instance = std::make_unique<VertexShaderCache>();
|
||||
GeometryShaderCache::s_instance = std::make_unique<GeometryShaderCache>();
|
||||
PixelShaderCache::s_instance = std::make_unique<PixelShaderCache>();
|
||||
return true;
|
||||
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
|
||||
return g_shader_cache->Initialize();
|
||||
}
|
||||
|
||||
void VideoBackend::Shutdown()
|
||||
{
|
||||
g_shader_cache->Shutdown();
|
||||
g_renderer->Shutdown();
|
||||
|
||||
PixelShaderCache::s_instance.reset();
|
||||
VertexShaderCache::s_instance.reset();
|
||||
GeometryShaderCache::s_instance.reset();
|
||||
|
||||
g_texture_cache.reset();
|
||||
g_perf_query.reset();
|
||||
g_vertex_manager.reset();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "VideoBackends/Null/VertexManager.h"
|
||||
|
||||
#include "VideoBackends/Null/ShaderCache.h"
|
||||
|
||||
#include "VideoCommon/IndexGenerator.h"
|
||||
#include "VideoCommon/NativeVertexFormat.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
|
@ -41,9 +39,6 @@ void VertexManager::ResetBuffer(u32 stride)
|
|||
|
||||
void VertexManager::vFlush()
|
||||
{
|
||||
VertexShaderCache::s_instance->SetShader(m_current_primitive_type);
|
||||
GeometryShaderCache::s_instance->SetShader(m_current_primitive_type);
|
||||
PixelShaderCache::s_instance->SetShader(m_current_primitive_type);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#include "VideoCommon/ImageWrite.h"
|
||||
#include "VideoCommon/PixelShaderManager.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
@ -43,8 +41,6 @@ namespace OGL
|
|||
{
|
||||
static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024;
|
||||
|
||||
std::unique_ptr<ProgramShaderCache::SharedContextAsyncShaderCompiler>
|
||||
ProgramShaderCache::s_async_compiler;
|
||||
u32 ProgramShaderCache::s_ubo_buffer_size;
|
||||
s32 ProgramShaderCache::s_ubo_align;
|
||||
GLuint ProgramShaderCache::s_attributeless_VBO = 0;
|
||||
|
@ -54,17 +50,9 @@ GLuint ProgramShaderCache::s_last_VAO = 0;
|
|||
static std::unique_ptr<StreamBuffer> s_buffer;
|
||||
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;
|
||||
ProgramShaderCache::PCache ProgramShaderCache::pshaders;
|
||||
ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders;
|
||||
ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms;
|
||||
std::mutex ProgramShaderCache::pipelineprogramlock;
|
||||
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 GetGLSLVersionString()
|
||||
|
@ -270,143 +258,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,
|
||||
const std::string& pcode, const std::string& gcode)
|
||||
{
|
||||
|
@ -620,11 +471,6 @@ bool ProgramShaderCache::CheckProgramLinkResult(GLuint id, const std::string& vc
|
|||
return true;
|
||||
}
|
||||
|
||||
ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram()
|
||||
{
|
||||
return *last_entry;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Init()
|
||||
{
|
||||
// We have to get the UBO alignment here because
|
||||
|
@ -642,93 +488,14 @@ void ProgramShaderCache::Init()
|
|||
// Then once more to get bytes
|
||||
s_buffer = StreamBuffer::Create(GL_UNIFORM_BUFFER, UBO_LENGTH);
|
||||
|
||||
// The GPU shader code appears to be context-specific on Mesa/i965.
|
||||
// This means that if we compiled the ubershaders asynchronously, they will be recompiled
|
||||
// on the main thread the first time they are used, causing stutter. Nouveau has been
|
||||
// reported to crash if draw calls are invoked on the shared context threads. For now,
|
||||
// disable asynchronous compilation on Mesa.
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION))
|
||||
s_async_compiler = std::make_unique<SharedContextAsyncShaderCompiler>();
|
||||
|
||||
// Read our shader cache, only if supported and enabled
|
||||
if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache)
|
||||
LoadProgramBinaries();
|
||||
|
||||
CreateHeader();
|
||||
CreateAttributelessVAO();
|
||||
|
||||
CurrentProgram = 0;
|
||||
last_entry = nullptr;
|
||||
last_uber_entry = nullptr;
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
{
|
||||
if (s_async_compiler)
|
||||
s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads());
|
||||
PrecompileUberShaders();
|
||||
}
|
||||
|
||||
if (s_async_compiler)
|
||||
{
|
||||
// No point using the async compiler without workers.
|
||||
s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
|
||||
if (!s_async_compiler->HasWorkerThreads())
|
||||
s_async_compiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::RetrieveAsyncShaders()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Reload()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
{
|
||||
s_async_compiler->WaitUntilCompletion();
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
const bool use_cache = g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache;
|
||||
if (use_cache)
|
||||
SaveProgramBinaries();
|
||||
|
||||
s_program_disk_cache.Close();
|
||||
s_uber_program_disk_cache.Close();
|
||||
DestroyShaders();
|
||||
|
||||
if (use_cache)
|
||||
LoadProgramBinaries();
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileUberShaders();
|
||||
|
||||
CurrentProgram = 0;
|
||||
last_entry = nullptr;
|
||||
last_uber_entry = nullptr;
|
||||
last_uid = {};
|
||||
last_uber_uid = {};
|
||||
}
|
||||
|
||||
void ProgramShaderCache::Shutdown()
|
||||
{
|
||||
if (s_async_compiler)
|
||||
{
|
||||
s_async_compiler->WaitUntilCompletion();
|
||||
s_async_compiler->StopWorkerThreads();
|
||||
s_async_compiler->RetrieveWorkItems();
|
||||
s_async_compiler.reset();
|
||||
}
|
||||
|
||||
// store all shaders in cache on disk
|
||||
if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache)
|
||||
SaveProgramBinaries();
|
||||
s_program_disk_cache.Close();
|
||||
s_uber_program_disk_cache.Close();
|
||||
|
||||
DestroyShaders();
|
||||
s_buffer.reset();
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
@ -781,134 +548,6 @@ void ProgramShaderCache::InvalidateLastProgram()
|
|||
CurrentProgram = 0;
|
||||
}
|
||||
|
||||
GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size)
|
||||
{
|
||||
const u8* binary = value + sizeof(GLenum);
|
||||
GLint binary_size = value_size - sizeof(GLenum);
|
||||
GLenum prog_format;
|
||||
std::memcpy(&prog_format, value, sizeof(GLenum));
|
||||
|
||||
GLuint progid = glCreateProgram();
|
||||
glProgramBinary(progid, prog_format, binary, binary_size);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(progid, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glDeleteProgram(progid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return progid;
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value,
|
||||
u32 value_size)
|
||||
{
|
||||
entry->in_cache = true;
|
||||
entry->pending = false;
|
||||
entry->shader.glprogid = CreateProgramFromBinary(value, value_size);
|
||||
if (entry->shader.glprogid == 0)
|
||||
return false;
|
||||
|
||||
entry->shader.SetProgramVariables();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::LoadProgramBinaries()
|
||||
{
|
||||
GLint Supported;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &Supported);
|
||||
if (!Supported)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "GL_ARB_get_program_binary is supported, but no binary format is known. So "
|
||||
"disable shader cache.");
|
||||
g_ogl_config.bSupportsGLSLCache = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load game-specific shaders.
|
||||
std::string cache_filename =
|
||||
GetDiskShaderCacheFileName(APIType::OpenGL, "ProgramBinaries", true, true);
|
||||
ProgramShaderCacheInserter<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* pixel_shader)
|
||||
|
@ -1144,346 +783,4 @@ void ProgramShaderCache::CreateHeader()
|
|||
v > GlslEs300 ? "precision highp sampler2DMS;" : "",
|
||||
v >= GlslEs310 ? "precision highp image2DArray;" : "");
|
||||
}
|
||||
|
||||
void ProgramShaderCache::PrecompileUberShaders()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
|
||||
UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
|
||||
// UIDs must have compatible texgens, a mismatching combination will never be queried.
|
||||
if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
|
||||
if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
UBERSHADERUID uid;
|
||||
std::memcpy(&uid.vuid, &vuid, sizeof(uid.vuid));
|
||||
std::memcpy(&uid.puid, &puid, sizeof(uid.puid));
|
||||
std::memcpy(&uid.guid, &guid, sizeof(uid.guid));
|
||||
|
||||
// The ubershader may already exist if shader caching is enabled.
|
||||
if (!success || ubershaders.find(uid) != ubershaders.end())
|
||||
return;
|
||||
|
||||
PCacheEntry& entry = ubershaders[uid];
|
||||
entry.in_cache = false;
|
||||
entry.pending = false;
|
||||
|
||||
// Multi-context path?
|
||||
if (s_async_compiler)
|
||||
{
|
||||
entry.pending = true;
|
||||
s_async_compiler->QueueWorkItem(
|
||||
s_async_compiler->CreateWorkItem<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.");
|
||||
delete ctx_data;
|
||||
return false;
|
||||
}
|
||||
|
||||
*param = ctx_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param)
|
||||
{
|
||||
SharedContextData* ctx_data = reinterpret_cast<SharedContextData*>(param);
|
||||
if (!ctx_data->context->MakeCurrent())
|
||||
{
|
||||
PanicAlert("Failed to make shared context current.");
|
||||
ctx_data->context->Shutdown();
|
||||
delete ctx_data;
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatePrerenderArrays(ctx_data);
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::DestroyPrerenderArrays(SharedContextData* data)
|
||||
{
|
||||
if (data->prerender_VAO)
|
||||
{
|
||||
glDeleteVertexArrays(1, &data->prerender_VAO);
|
||||
data->prerender_VAO = 0;
|
||||
}
|
||||
if (data->prerender_VBO)
|
||||
{
|
||||
glDeleteBuffers(1, &data->prerender_VBO);
|
||||
data->prerender_VBO = 0;
|
||||
}
|
||||
if (data->prerender_IBO)
|
||||
{
|
||||
glDeleteBuffers(1, &data->prerender_IBO);
|
||||
data->prerender_IBO = 0;
|
||||
}
|
||||
if (data->prerender_FBO)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &data->prerender_FBO);
|
||||
data->prerender_FBO = 0;
|
||||
}
|
||||
if (data->prerender_FBO_tex)
|
||||
{
|
||||
glDeleteTextures(1, &data->prerender_FBO_tex);
|
||||
data->prerender_FBO_tex = 0;
|
||||
}
|
||||
if (data->prerender_FBO_depth)
|
||||
{
|
||||
glDeleteTextures(1, &data->prerender_FBO_depth);
|
||||
data->prerender_FBO_depth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramShaderCache::DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type)
|
||||
{
|
||||
// This is called on a worker thread, so we don't want to use the normal binding process.
|
||||
glUseProgram(shader.glprogid);
|
||||
|
||||
// The number of primitives drawn depends on the type.
|
||||
switch (primitive_type)
|
||||
{
|
||||
case PrimitiveType::Points:
|
||||
glDrawElements(GL_POINTS, 1, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::Lines:
|
||||
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::Triangles:
|
||||
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
case PrimitiveType::TriangleStrip:
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 3, GL_UNSIGNED_SHORT, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
// Has to be finished by the time the main thread picks it up.
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
}
|
||||
} // namespace OGL
|
||||
|
|
|
@ -11,16 +11,6 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "Common/GL/GLUtil.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
class cInterfaceBase;
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
|
@ -28,41 +18,6 @@ class OGLShader;
|
|||
class GLVertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
class SHADERUID
|
||||
{
|
||||
public:
|
||||
VertexShaderUid vuid;
|
||||
PixelShaderUid puid;
|
||||
GeometryShaderUid guid;
|
||||
|
||||
bool operator<(const SHADERUID& r) const
|
||||
{
|
||||
return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid);
|
||||
}
|
||||
|
||||
bool operator==(const SHADERUID& r) const
|
||||
{
|
||||
return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid);
|
||||
}
|
||||
};
|
||||
class UBERSHADERUID
|
||||
{
|
||||
public:
|
||||
UberShader::VertexShaderUid vuid;
|
||||
UberShader::PixelShaderUid puid;
|
||||
GeometryShaderUid guid;
|
||||
|
||||
bool operator<(const UBERSHADERUID& r) const
|
||||
{
|
||||
return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid);
|
||||
}
|
||||
|
||||
bool operator==(const UBERSHADERUID& r) const
|
||||
{
|
||||
return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid);
|
||||
}
|
||||
};
|
||||
|
||||
struct SHADER
|
||||
{
|
||||
void Destroy()
|
||||
|
@ -112,18 +67,6 @@ struct PipelineProgram
|
|||
class ProgramShaderCache
|
||||
{
|
||||
public:
|
||||
struct PCacheEntry
|
||||
{
|
||||
SHADER shader;
|
||||
bool in_cache;
|
||||
bool pending;
|
||||
|
||||
void Destroy() { shader.Destroy(); }
|
||||
};
|
||||
|
||||
static PCacheEntry GetShaderProgram();
|
||||
static SHADER* SetShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format);
|
||||
static SHADER* SetUberShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format);
|
||||
static void BindVertexFormat(const GLVertexFormat* vertex_format);
|
||||
static void InvalidateVertexFormat();
|
||||
static void InvalidateLastProgram();
|
||||
|
@ -141,11 +84,8 @@ public:
|
|||
static void UploadConstants();
|
||||
|
||||
static void Init();
|
||||
static void Reload();
|
||||
static void Shutdown();
|
||||
static void CreateHeader();
|
||||
static void RetrieveAsyncShaders();
|
||||
static void PrecompileUberShaders();
|
||||
|
||||
static const PipelineProgram* GetPipelineProgram(const OGLShader* vertex_shader,
|
||||
const OGLShader* geometry_shader,
|
||||
|
@ -153,103 +93,15 @@ public:
|
|||
static void ReleasePipelineProgram(const PipelineProgram* prog);
|
||||
|
||||
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>,
|
||||
PipelineProgramKeyHash>
|
||||
PipelineProgramMap;
|
||||
|
||||
static void CreateAttributelessVAO();
|
||||
static GLuint CreateProgramFromBinary(const u8* value, u32 value_size);
|
||||
static bool CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value, u32 value_size);
|
||||
static void LoadProgramBinaries();
|
||||
static void SaveProgramBinaries();
|
||||
static void DestroyShaders();
|
||||
static void CreatePrerenderArrays(SharedContextData* data);
|
||||
static void DestroyPrerenderArrays(SharedContextData* data);
|
||||
static void DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type);
|
||||
|
||||
static PCache pshaders;
|
||||
static UberPCache ubershaders;
|
||||
static PipelineProgramMap pipelineprograms;
|
||||
static std::mutex pipelineprogramlock;
|
||||
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 s32 s_ubo_align;
|
||||
|
||||
|
|
|
@ -1465,7 +1465,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
|
||||
// Clean out old stuff from caches. It's not worth it to clean out the shader caches.
|
||||
g_texture_cache->Cleanup(frameCount);
|
||||
ProgramShaderCache::RetrieveAsyncShaders();
|
||||
|
||||
RestoreAPIState();
|
||||
|
||||
|
@ -1479,8 +1478,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
g_sampler_cache->Clear();
|
||||
|
||||
// Invalidate shader cache when the host config changes.
|
||||
if (CheckForHostConfigChanges())
|
||||
ProgramShaderCache::Reload();
|
||||
CheckForHostConfigChanges();
|
||||
|
||||
// For testing zbuffer targets.
|
||||
// Renderer::SetZBufferRender();
|
||||
|
@ -1602,21 +1600,6 @@ void Renderer::ApplyDepthState(const DepthState& state)
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::SetRasterizationState(const RasterizationState& 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)
|
||||
{
|
||||
// Not all shader changes currently go through SetPipeline, so we can't
|
||||
|
|
|
@ -105,10 +105,7 @@ public:
|
|||
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value = {},
|
||||
float depth_value = 0.0f) override;
|
||||
void SetBlendingState(const BlendingState& state) override;
|
||||
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
|
||||
void SetRasterizationState(const RasterizationState& state) override;
|
||||
void SetDepthState(const DepthState& state) override;
|
||||
void SetTexture(u32 index, const AbstractTexture* texture) override;
|
||||
void SetSamplerState(u32 index, const SamplerState& state) override;
|
||||
void UnbindTexture(const AbstractTexture* texture) override;
|
||||
|
|
|
@ -162,8 +162,6 @@ void VertexManager::vFlush()
|
|||
GLVertexFormat* nativeVertexFmt = (GLVertexFormat*)VertexLoaderManager::GetCurrentVertexFormat();
|
||||
u32 stride = nativeVertexFmt->GetVertexStride();
|
||||
|
||||
ProgramShaderCache::SetShader(m_current_primitive_type, nativeVertexFmt);
|
||||
|
||||
PrepareDrawBuffers(stride);
|
||||
|
||||
// upload global constants
|
||||
|
@ -174,7 +172,11 @@ void VertexManager::vFlush()
|
|||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
Draw(stride);
|
||||
if (m_current_pipeline_object)
|
||||
{
|
||||
g_renderer->SetPipeline(m_current_pipeline_object);
|
||||
Draw(stride);
|
||||
}
|
||||
|
||||
if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
|
|
|
@ -175,17 +175,20 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
ProgramShaderCache::Init();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
g_sampler_cache = std::make_unique<SamplerCache>();
|
||||
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
|
||||
static_cast<Renderer*>(g_renderer.get())->Init();
|
||||
TextureConverter::Init();
|
||||
BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight());
|
||||
return true;
|
||||
return g_shader_cache->Initialize();
|
||||
}
|
||||
|
||||
void VideoBackend::Shutdown()
|
||||
{
|
||||
g_shader_cache->Shutdown();
|
||||
g_renderer->Shutdown();
|
||||
BoundingBox::Shutdown();
|
||||
TextureConverter::Shutdown();
|
||||
g_shader_cache.reset();
|
||||
g_sampler_cache.reset();
|
||||
g_texture_cache.reset();
|
||||
ProgramShaderCache::Shutdown();
|
||||
|
|
|
@ -96,11 +96,15 @@ bool VideoSoftware::Initialize(void* window_handle)
|
|||
g_vertex_manager = std::make_unique<SWVertexLoader>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
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()
|
||||
{
|
||||
if (g_shader_cache)
|
||||
g_shader_cache->Shutdown();
|
||||
|
||||
if (g_renderer)
|
||||
g_renderer->Shutdown();
|
||||
|
||||
|
|
|
@ -48,8 +48,7 @@ enum DESCRIPTOR_SET_BIND_POINT
|
|||
// - Standard
|
||||
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
|
||||
// - 8 combined image samplers (accessible from PS)
|
||||
// - BBox Enabled
|
||||
// - Same as standard, plus a single SSBO accessible from PS
|
||||
// - 1 SSBO accessible from PS if supported
|
||||
// - Push Constant
|
||||
// - Same as standard, plus 128 bytes of push constants, accessible from all stages.
|
||||
// - Texture Decoding
|
||||
|
@ -67,7 +66,6 @@ enum DESCRIPTOR_SET_BIND_POINT
|
|||
enum PIPELINE_LAYOUT
|
||||
{
|
||||
PIPELINE_LAYOUT_STANDARD,
|
||||
PIPELINE_LAYOUT_BBOX,
|
||||
PIPELINE_LAYOUT_PUSH_CONSTANT,
|
||||
PIPELINE_LAYOUT_TEXTURE_CONVERSION,
|
||||
PIPELINE_LAYOUT_UTILITY,
|
||||
|
|
|
@ -109,6 +109,9 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
|||
static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = {
|
||||
0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT};
|
||||
|
||||
// The geometry shader buffer must be last in this binding set, as we don't include it
|
||||
// if geometry shaders are not supported by the device. See the decrement below.
|
||||
static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = {
|
||||
{UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT},
|
||||
|
@ -139,7 +142,7 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
|||
{7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT},
|
||||
};
|
||||
|
||||
static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
|
||||
VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
|
||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings},
|
||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
|
@ -153,6 +156,10 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
|||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(compute_set_bindings)), compute_set_bindings}};
|
||||
|
||||
// Don't set the GS bit if geometry shaders aren't available.
|
||||
if (!g_vulkan_context->SupportsGeometryShaders())
|
||||
create_infos[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS].bindingCount--;
|
||||
|
||||
for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++)
|
||||
{
|
||||
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i],
|
||||
|
@ -180,11 +187,10 @@ bool ObjectCache::CreatePipelineLayouts()
|
|||
{
|
||||
VkResult res;
|
||||
|
||||
// Descriptor sets for each pipeline layout
|
||||
// Descriptor sets for each pipeline layout.
|
||||
// In the standard set, the SSBO must be the last descriptor, as we do not include it
|
||||
// when fragment stores and atomics are not supported by the device.
|
||||
VkDescriptorSetLayout standard_sets[] = {
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
|
||||
VkDescriptorSetLayout bbox_sets[] = {
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
|
||||
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]};
|
||||
|
@ -207,10 +213,6 @@ bool ObjectCache::CreatePipelineLayouts()
|
|||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
|
||||
|
||||
// BBox
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(bbox_sets)), bbox_sets, 0, nullptr},
|
||||
|
||||
// Push Constant
|
||||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range},
|
||||
|
@ -228,6 +230,10 @@ bool ObjectCache::CreatePipelineLayouts()
|
|||
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||
static_cast<u32>(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}};
|
||||
|
||||
// If bounding box is unsupported, don't bother with the SSBO descriptor set.
|
||||
if (!g_vulkan_context->SupportsBoundingBox())
|
||||
pipeline_layout_info[PIPELINE_LAYOUT_STANDARD].setLayoutCount--;
|
||||
|
||||
for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++)
|
||||
{
|
||||
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i],
|
||||
|
|
|
@ -224,7 +224,7 @@ std::tuple<VkBuffer, u32> Renderer::UpdateUtilityUniformBuffer(const void* unifo
|
|||
|
||||
void Renderer::SetPipeline(const AbstractPipeline* pipeline)
|
||||
{
|
||||
m_graphics_pipeline = static_cast<const VKPipeline*>(pipeline);
|
||||
StateTracker::GetInstance()->SetPipeline(static_cast<const VKPipeline*>(pipeline));
|
||||
}
|
||||
|
||||
void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
|
||||
|
@ -305,7 +305,7 @@ void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, cons
|
|||
// Build commands.
|
||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_graphics_pipeline->GetPipeline());
|
||||
StateTracker::GetInstance()->GetPipeline()->GetVkPipeline());
|
||||
if (vertex_buffer != VK_NULL_HANDLE)
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset);
|
||||
|
||||
|
@ -759,9 +759,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
|
||||
// Pull in now-ready async shaders.
|
||||
g_shader_cache->RetrieveAsyncShaders();
|
||||
}
|
||||
|
||||
void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region)
|
||||
|
@ -975,10 +972,8 @@ void Renderer::CheckForConfigChanges()
|
|||
RecreateEFBFramebuffer();
|
||||
RecompileShaders();
|
||||
FramebufferManager::GetInstance()->RecompileShaders();
|
||||
g_shader_cache->ReloadShaderAndPipelineCaches();
|
||||
g_shader_cache->ReloadPipelineCache();
|
||||
g_shader_cache->RecompileSharedShaders();
|
||||
StateTracker::GetInstance()->InvalidateShaderPointers();
|
||||
StateTracker::GetInstance()->ReloadPipelineUIDCache();
|
||||
}
|
||||
|
||||
// For vsync, we need to change the present mode, which means recreating the swap chain.
|
||||
|
@ -1021,8 +1016,6 @@ void Renderer::BindEFBToStateTracker()
|
|||
FramebufferManager::GetInstance()->GetEFBClearRenderPass());
|
||||
StateTracker::GetInstance()->SetFramebuffer(
|
||||
FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size);
|
||||
StateTracker::GetInstance()->SetMultisamplingstate(
|
||||
FramebufferManager::GetInstance()->GetEFBMultisamplingState());
|
||||
m_current_framebuffer = nullptr;
|
||||
m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth();
|
||||
m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight();
|
||||
|
@ -1125,21 +1118,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
|||
num_clear_values);
|
||||
}
|
||||
|
||||
void Renderer::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetRasterizationState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetDepthState(const DepthState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetDepthState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetBlendingState(const BlendingState& state)
|
||||
{
|
||||
StateTracker::GetInstance()->SetBlendState(state);
|
||||
}
|
||||
|
||||
void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
|
||||
{
|
||||
// Texture should always be in SHADER_READ_ONLY layout prior to use.
|
||||
|
|
|
@ -77,10 +77,7 @@ public:
|
|||
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value = {},
|
||||
float depth_value = 0.0f) override;
|
||||
void SetBlendingState(const BlendingState& state) override;
|
||||
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
|
||||
void SetRasterizationState(const RasterizationState& state) override;
|
||||
void SetDepthState(const DepthState& state) override;
|
||||
void SetTexture(u32 index, const AbstractTexture* texture) override;
|
||||
void SetSamplerState(u32 index, const SamplerState& state) override;
|
||||
void UnbindTexture(const AbstractTexture* texture) override;
|
||||
|
@ -135,6 +132,5 @@ private:
|
|||
|
||||
// Shaders used for clear/blit.
|
||||
VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE;
|
||||
const VKPipeline* m_graphics_pipeline = nullptr;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,12 +23,7 @@
|
|||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
|
@ -41,7 +36,6 @@ ShaderCache::ShaderCache()
|
|||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
DestroyPipelineCache();
|
||||
DestroyShaderCaches();
|
||||
DestroySharedShaders();
|
||||
}
|
||||
|
||||
|
@ -49,7 +43,6 @@ bool ShaderCache::Initialize()
|
|||
{
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
LoadShaderCaches();
|
||||
if (!LoadPipelineCache())
|
||||
return false;
|
||||
}
|
||||
|
@ -62,21 +55,11 @@ bool ShaderCache::Initialize()
|
|||
if (!CompileSharedShaders())
|
||||
return false;
|
||||
|
||||
m_async_shader_compiler = std::make_unique<VideoCommon::AsyncShaderCompiler>();
|
||||
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ?
|
||||
g_ActiveConfig.GetShaderPrecompilerThreads() :
|
||||
g_ActiveConfig.GetShaderCompilerThreads());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::Shutdown()
|
||||
{
|
||||
if (m_async_shader_compiler)
|
||||
{
|
||||
m_async_shader_compiler->StopWorkerThreads();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShaderCache && m_pipeline_cache != VK_NULL_HANDLE)
|
||||
SavePipelineCache();
|
||||
}
|
||||
|
@ -392,40 +375,14 @@ VkPipeline ShaderCache::CreatePipeline(const PipelineInfo& info)
|
|||
}
|
||||
|
||||
VkPipeline ShaderCache::GetPipeline(const PipelineInfo& info)
|
||||
{
|
||||
return GetPipelineWithCacheResult(info).first;
|
||||
}
|
||||
|
||||
std::pair<VkPipeline, bool> ShaderCache::GetPipelineWithCacheResult(const PipelineInfo& info)
|
||||
{
|
||||
auto iter = m_pipeline_objects.find(info);
|
||||
if (iter != m_pipeline_objects.end())
|
||||
{
|
||||
// If it's background compiling, ignore it, and recompile it synchronously.
|
||||
if (!iter->second.second)
|
||||
return std::make_pair(iter->second.first, true);
|
||||
else
|
||||
m_pipeline_objects.erase(iter);
|
||||
}
|
||||
return iter->second;
|
||||
|
||||
VkPipeline pipeline = CreatePipeline(info);
|
||||
m_pipeline_objects.emplace(info, std::make_pair(pipeline, false));
|
||||
_assert_(pipeline != VK_NULL_HANDLE);
|
||||
return {pipeline, false};
|
||||
}
|
||||
|
||||
std::pair<std::pair<VkPipeline, bool>, bool>
|
||||
ShaderCache::GetPipelineWithCacheResultAsync(const PipelineInfo& info)
|
||||
{
|
||||
auto iter = m_pipeline_objects.find(info);
|
||||
if (iter != m_pipeline_objects.end())
|
||||
return std::make_pair(iter->second, true);
|
||||
|
||||
// Kick a job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<PipelineCompilerWorkItem>(info));
|
||||
m_pipeline_objects.emplace(info, std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair(std::make_pair(static_cast<VkPipeline>(VK_NULL_HANDLE), true), false);
|
||||
m_pipeline_objects.emplace(info, pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VkPipeline ShaderCache::CreateComputePipeline(const ComputePipelineInfo& info)
|
||||
|
@ -465,11 +422,10 @@ VkPipeline ShaderCache::GetComputePipeline(const ComputePipelineInfo& info)
|
|||
|
||||
void ShaderCache::ClearPipelineCache()
|
||||
{
|
||||
// TODO: Stop any async compiling happening.
|
||||
for (const auto& it : m_pipeline_objects)
|
||||
{
|
||||
if (it.second.first != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second.first, nullptr);
|
||||
if (it.second != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr);
|
||||
}
|
||||
m_pipeline_objects.clear();
|
||||
|
||||
|
@ -673,266 +629,6 @@ void ShaderCache::SavePipelineCache()
|
|||
disk_cache.Close();
|
||||
}
|
||||
|
||||
// Cache inserter that is called back when reading from the file
|
||||
template <typename Uid>
|
||||
struct ShaderCacheReader : public LinearDiskCacheReader<Uid, u32>
|
||||
{
|
||||
ShaderCacheReader(std::map<Uid, std::pair<VkShaderModule, bool>>& shader_map)
|
||||
: m_shader_map(shader_map)
|
||||
{
|
||||
}
|
||||
void Read(const Uid& key, const u32* value, u32 value_size) override
|
||||
{
|
||||
// We don't insert null modules into the shader map since creation could succeed later on.
|
||||
// e.g. we're generating bad code, but fix this in a later version, and for some reason
|
||||
// the cache is not invalidated.
|
||||
VkShaderModule module = Util::CreateShaderModule(value, value_size);
|
||||
if (module == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
m_shader_map.emplace(key, std::make_pair(module, false));
|
||||
}
|
||||
|
||||
std::map<Uid, std::pair<VkShaderModule, bool>>& m_shader_map;
|
||||
};
|
||||
|
||||
void ShaderCache::LoadShaderCaches()
|
||||
{
|
||||
ShaderCacheReader<VertexShaderUid> vs_reader(m_vs_cache.shader_map);
|
||||
m_vs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "VS", true, true),
|
||||
vs_reader);
|
||||
|
||||
ShaderCacheReader<PixelShaderUid> ps_reader(m_ps_cache.shader_map);
|
||||
m_ps_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "PS", true, true),
|
||||
ps_reader);
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
ShaderCacheReader<GeometryShaderUid> gs_reader(m_gs_cache.shader_map);
|
||||
m_gs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "GS", true, true),
|
||||
gs_reader);
|
||||
}
|
||||
|
||||
ShaderCacheReader<UberShader::VertexShaderUid> uber_vs_reader(m_uber_vs_cache.shader_map);
|
||||
m_uber_vs_cache.disk_cache.OpenAndRead(
|
||||
GetDiskShaderCacheFileName(APIType::Vulkan, "UberVS", false, true), uber_vs_reader);
|
||||
ShaderCacheReader<UberShader::PixelShaderUid> uber_ps_reader(m_uber_ps_cache.shader_map);
|
||||
m_uber_ps_cache.disk_cache.OpenAndRead(
|
||||
GetDiskShaderCacheFileName(APIType::Vulkan, "UberPS", false, true), uber_ps_reader);
|
||||
|
||||
SETSTAT(stats.numPixelShadersCreated, static_cast<int>(m_ps_cache.shader_map.size()));
|
||||
SETSTAT(stats.numPixelShadersAlive, static_cast<int>(m_ps_cache.shader_map.size()));
|
||||
SETSTAT(stats.numVertexShadersCreated, static_cast<int>(m_vs_cache.shader_map.size()));
|
||||
SETSTAT(stats.numVertexShadersAlive, static_cast<int>(m_vs_cache.shader_map.size()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void DestroyShaderCache(T& cache)
|
||||
{
|
||||
cache.disk_cache.Sync();
|
||||
cache.disk_cache.Close();
|
||||
for (const auto& it : cache.shader_map)
|
||||
{
|
||||
if (it.second.first != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second.first, nullptr);
|
||||
}
|
||||
cache.shader_map.clear();
|
||||
}
|
||||
|
||||
void ShaderCache::DestroyShaderCaches()
|
||||
{
|
||||
DestroyShaderCache(m_vs_cache);
|
||||
DestroyShaderCache(m_ps_cache);
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
DestroyShaderCache(m_gs_cache);
|
||||
|
||||
DestroyShaderCache(m_uber_vs_cache);
|
||||
DestroyShaderCache(m_uber_ps_cache);
|
||||
|
||||
SETSTAT(stats.numPixelShadersCreated, 0);
|
||||
SETSTAT(stats.numPixelShadersAlive, 0);
|
||||
SETSTAT(stats.numVertexShadersCreated, 0);
|
||||
SETSTAT(stats.numVertexShadersAlive, 0);
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetVertexShaderForUid(const VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_vs_cache.shader_map.find(uid);
|
||||
if (it != m_vs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_vs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numVertexShadersCreated);
|
||||
INCSTAT(stats.numVertexShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetGeometryShaderForUid(const GeometryShaderUid& uid)
|
||||
{
|
||||
_assert_(g_vulkan_context->SupportsGeometryShaders());
|
||||
auto it = m_gs_cache.shader_map.find(uid);
|
||||
if (it != m_gs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_gs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GenerateGeometryShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileGeometryShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
m_gs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_gs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetPixelShaderForUid(const PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_ps_cache.shader_map.find(uid);
|
||||
if (it != m_ps_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_ps_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numPixelShadersCreated);
|
||||
INCSTAT(stats.numPixelShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_uber_vs_cache.shader_map.find(uid);
|
||||
if (it != m_uber_vs_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_uber_vs_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code = UberShader::GenVertexShader(
|
||||
APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_uber_vs_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numVertexShadersCreated);
|
||||
INCSTAT(stats.numVertexShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_uber_vs_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_uber_ps_cache.shader_map.find(uid);
|
||||
if (it != m_uber_ps_cache.shader_map.end())
|
||||
{
|
||||
// If it's pending, compile it synchronously.
|
||||
if (!it->second.second)
|
||||
return it->second.first;
|
||||
else
|
||||
m_uber_ps_cache.shader_map.erase(it);
|
||||
}
|
||||
|
||||
// Not in the cache, so compile the shader.
|
||||
ShaderCompiler::SPIRVCodeVector spv;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ShaderCode source_code =
|
||||
UberShader::GenPixelShader(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData());
|
||||
if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(),
|
||||
source_code.GetBuffer().length()))
|
||||
{
|
||||
module = Util::CreateShaderModule(spv.data(), spv.size());
|
||||
|
||||
// Append to shader cache if it created successfully.
|
||||
if (module != VK_NULL_HANDLE)
|
||||
{
|
||||
m_uber_ps_cache.disk_cache.Append(uid, spv.data(), static_cast<u32>(spv.size()));
|
||||
INCSTAT(stats.numPixelShadersCreated);
|
||||
INCSTAT(stats.numPixelShadersAlive);
|
||||
}
|
||||
}
|
||||
|
||||
// We still insert null entries to prevent further compilation attempts.
|
||||
m_uber_ps_cache.shader_map.emplace(uid, std::make_pair(module, false));
|
||||
return module;
|
||||
}
|
||||
|
||||
void ShaderCache::RecompileSharedShaders()
|
||||
{
|
||||
DestroySharedShaders();
|
||||
|
@ -940,27 +636,15 @@ void ShaderCache::RecompileSharedShaders()
|
|||
PanicAlert("Failed to recompile shared shaders.");
|
||||
}
|
||||
|
||||
void ShaderCache::ReloadShaderAndPipelineCaches()
|
||||
void ShaderCache::ReloadPipelineCache()
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
|
||||
SavePipelineCache();
|
||||
DestroyShaderCaches();
|
||||
DestroyPipelineCache();
|
||||
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
LoadShaderCaches();
|
||||
LoadPipelineCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatePipelineCache();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
PrecompileUberShaders();
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetUtilityShaderHeader() const
|
||||
|
@ -1160,203 +844,4 @@ void ShaderCache::DestroySharedShaders()
|
|||
DestroyShader(m_screen_quad_geometry_shader);
|
||||
DestroyShader(m_passthrough_geometry_shader);
|
||||
}
|
||||
|
||||
void ShaderCache::CreateDummyPipeline(const UberShader::VertexShaderUid& vuid,
|
||||
const GeometryShaderUid& guid,
|
||||
const UberShader::PixelShaderUid& puid)
|
||||
{
|
||||
PortableVertexDeclaration vertex_decl;
|
||||
std::memset(&vertex_decl, 0, sizeof(vertex_decl));
|
||||
|
||||
PipelineInfo pinfo;
|
||||
pinfo.vertex_format =
|
||||
static_cast<const VertexFormat*>(VertexLoaderManager::GetUberVertexFormat(vertex_decl));
|
||||
pinfo.pipeline_layout = g_object_cache->GetPipelineLayout(
|
||||
g_ActiveConfig.bBBoxEnable && g_ActiveConfig.BBoxUseFragmentShaderImplementation() ?
|
||||
PIPELINE_LAYOUT_BBOX :
|
||||
PIPELINE_LAYOUT_STANDARD);
|
||||
pinfo.vs = GetVertexUberShaderForUid(vuid);
|
||||
pinfo.gs = (!guid.GetUidData()->IsPassthrough() && g_vulkan_context->SupportsGeometryShaders()) ?
|
||||
GetGeometryShaderForUid(guid) :
|
||||
VK_NULL_HANDLE;
|
||||
pinfo.ps = GetPixelUberShaderForUid(puid);
|
||||
pinfo.render_pass = FramebufferManager::GetInstance()->GetEFBLoadRenderPass();
|
||||
pinfo.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex;
|
||||
pinfo.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex;
|
||||
pinfo.blend_state.hex = RenderState::GetNoBlendingBlendState().hex;
|
||||
pinfo.multisampling_state.hex = FramebufferManager::GetInstance()->GetEFBMultisamplingState().hex;
|
||||
pinfo.rasterization_state.primitive =
|
||||
static_cast<PrimitiveType>(guid.GetUidData()->primitive_type);
|
||||
GetPipelineWithCacheResultAsync(pinfo);
|
||||
}
|
||||
|
||||
void ShaderCache::PrecompileUberShaders()
|
||||
{
|
||||
UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) {
|
||||
UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) {
|
||||
// UIDs must have compatible texgens, a mismatching combination will never be queried.
|
||||
if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) {
|
||||
if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens)
|
||||
return;
|
||||
|
||||
CreateDummyPipeline(vuid, guid, puid);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
WaitForBackgroundCompilesToComplete();
|
||||
|
||||
// Switch to the runtime/background thread config.
|
||||
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
|
||||
}
|
||||
|
||||
void ShaderCache::WaitForBackgroundCompilesToComplete()
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) {
|
||||
Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(),
|
||||
static_cast<int>(completed), static_cast<int>(total));
|
||||
});
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
Host_UpdateProgressDialog("", -1, -1);
|
||||
}
|
||||
|
||||
void ShaderCache::RetrieveAsyncShaders()
|
||||
{
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
std::pair<VkShaderModule, bool> ShaderCache::GetVertexShaderForUidAsync(const VertexShaderUid& uid)
|
||||
{
|
||||
auto it = m_vs_cache.shader_map.find(uid);
|
||||
if (it != m_vs_cache.shader_map.end())
|
||||
return it->second;
|
||||
|
||||
// Kick a compile job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<VertexShaderCompilerWorkItem>(uid));
|
||||
m_vs_cache.shader_map.emplace(uid,
|
||||
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
|
||||
}
|
||||
|
||||
std::pair<VkShaderModule, bool> ShaderCache::GetPixelShaderForUidAsync(const PixelShaderUid& uid)
|
||||
{
|
||||
auto it = m_ps_cache.shader_map.find(uid);
|
||||
if (it != m_ps_cache.shader_map.end())
|
||||
return it->second;
|
||||
|
||||
// Kick a compile job off.
|
||||
m_async_shader_compiler->QueueWorkItem(
|
||||
m_async_shader_compiler->CreateWorkItem<PixelShaderCompilerWorkItem>(uid));
|
||||
m_ps_cache.shader_map.emplace(uid,
|
||||
std::make_pair(static_cast<VkShaderModule>(VK_NULL_HANDLE), true));
|
||||
return std::make_pair<VkShaderModule, bool>(VK_NULL_HANDLE, true);
|
||||
}
|
||||
|
||||
bool ShaderCache::VertexShaderCompilerWorkItem::Compile()
|
||||
{
|
||||
ShaderCode code =
|
||||
GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
|
||||
if (!ShaderCompiler::CompileVertexShader(&m_spirv, code.GetBuffer().c_str(),
|
||||
code.GetBuffer().length()))
|
||||
return true;
|
||||
|
||||
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::VertexShaderCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_vs_cache.shader_map.find(m_uid);
|
||||
if (it == g_shader_cache->m_vs_cache.shader_map.end())
|
||||
{
|
||||
g_shader_cache->m_vs_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
|
||||
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_module != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_module;
|
||||
it->second.second = false;
|
||||
g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
}
|
||||
|
||||
bool ShaderCache::PixelShaderCompilerWorkItem::Compile()
|
||||
{
|
||||
ShaderCode code =
|
||||
GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData());
|
||||
if (!ShaderCompiler::CompileFragmentShader(&m_spirv, code.GetBuffer().c_str(),
|
||||
code.GetBuffer().length()))
|
||||
return true;
|
||||
|
||||
m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::PixelShaderCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_ps_cache.shader_map.find(m_uid);
|
||||
if (it == g_shader_cache->m_ps_cache.shader_map.end())
|
||||
{
|
||||
g_shader_cache->m_ps_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false));
|
||||
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_module != VK_NULL_HANDLE)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_module;
|
||||
it->second.second = false;
|
||||
g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(),
|
||||
static_cast<u32>(m_spirv.size()));
|
||||
}
|
||||
|
||||
bool ShaderCache::PipelineCompilerWorkItem::Compile()
|
||||
{
|
||||
m_pipeline = g_shader_cache->CreatePipeline(m_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::PipelineCompilerWorkItem::Retrieve()
|
||||
{
|
||||
auto it = g_shader_cache->m_pipeline_objects.find(m_info);
|
||||
if (it == g_shader_cache->m_pipeline_objects.end())
|
||||
{
|
||||
g_shader_cache->m_pipeline_objects.emplace(m_info, std::make_pair(m_pipeline, false));
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread may have also compiled this shader.
|
||||
if (!it->second.second)
|
||||
{
|
||||
if (m_pipeline != VK_NULL_HANDLE)
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// No longer pending.
|
||||
it->second.first = m_pipeline;
|
||||
it->second.second = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,7 @@
|
|||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
|
@ -33,10 +27,6 @@ class CommandBufferManager;
|
|||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
class CommandBufferManager;
|
||||
class VertexFormat;
|
||||
class StreamBuffer;
|
||||
|
||||
struct PipelineInfo
|
||||
{
|
||||
// These are packed in descending order of size, to avoid any padding so that the structure
|
||||
|
@ -88,19 +78,6 @@ public:
|
|||
// Get utility shader header based on current config.
|
||||
std::string GetUtilityShaderHeader() const;
|
||||
|
||||
// Accesses ShaderGen shader caches
|
||||
VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid);
|
||||
VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid);
|
||||
VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid);
|
||||
|
||||
// Ubershader caches
|
||||
VkShaderModule GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid);
|
||||
VkShaderModule GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid);
|
||||
|
||||
// Accesses ShaderGen shader caches asynchronously
|
||||
std::pair<VkShaderModule, bool> GetVertexShaderForUidAsync(const VertexShaderUid& uid);
|
||||
std::pair<VkShaderModule, bool> GetPixelShaderForUidAsync(const PixelShaderUid& uid);
|
||||
|
||||
// Perform at startup, create descriptor layouts, compiles all static shaders.
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
@ -112,13 +89,6 @@ public:
|
|||
// Find a pipeline by the specified description, if not found, attempts to create it.
|
||||
VkPipeline GetPipeline(const PipelineInfo& info);
|
||||
|
||||
// Find a pipeline by the specified description, if not found, attempts to create it. If this
|
||||
// resulted in a pipeline being created, the second field of the return value will be false,
|
||||
// otherwise for a cache hit it will be true.
|
||||
std::pair<VkPipeline, bool> GetPipelineWithCacheResult(const PipelineInfo& info);
|
||||
std::pair<std::pair<VkPipeline, bool>, bool>
|
||||
GetPipelineWithCacheResultAsync(const PipelineInfo& info);
|
||||
|
||||
// Creates a compute pipeline, and does not track the handle.
|
||||
VkPipeline CreateComputePipeline(const ComputePipelineInfo& info);
|
||||
|
||||
|
@ -139,47 +109,22 @@ public:
|
|||
void RecompileSharedShaders();
|
||||
|
||||
// Reload pipeline cache. This will destroy all pipelines.
|
||||
void ReloadShaderAndPipelineCaches();
|
||||
void ReloadPipelineCache();
|
||||
|
||||
// Shared shader accessors
|
||||
VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; }
|
||||
VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; }
|
||||
VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; }
|
||||
VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; }
|
||||
void PrecompileUberShaders();
|
||||
void WaitForBackgroundCompilesToComplete();
|
||||
void RetrieveAsyncShaders();
|
||||
|
||||
private:
|
||||
bool CreatePipelineCache();
|
||||
bool LoadPipelineCache();
|
||||
bool ValidatePipelineCache(const u8* data, size_t data_length);
|
||||
void DestroyPipelineCache();
|
||||
void LoadShaderCaches();
|
||||
void DestroyShaderCaches();
|
||||
bool CompileSharedShaders();
|
||||
void DestroySharedShaders();
|
||||
|
||||
// We generate a dummy pipeline with some defaults in the blend/depth states,
|
||||
// that way the driver is forced to compile something (looking at you, NVIDIA).
|
||||
// It can then hopefully re-use part of this pipeline for others in the future.
|
||||
void CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, const GeometryShaderUid& guid,
|
||||
const UberShader::PixelShaderUid& puid);
|
||||
|
||||
template <typename Uid>
|
||||
struct ShaderModuleCache
|
||||
{
|
||||
std::map<Uid, std::pair<VkShaderModule, bool>> shader_map;
|
||||
LinearDiskCache<Uid, u32> disk_cache;
|
||||
};
|
||||
ShaderModuleCache<VertexShaderUid> m_vs_cache;
|
||||
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
|
||||
ShaderModuleCache<PixelShaderUid> m_ps_cache;
|
||||
ShaderModuleCache<UberShader::VertexShaderUid> m_uber_vs_cache;
|
||||
ShaderModuleCache<UberShader::PixelShaderUid> m_uber_ps_cache;
|
||||
|
||||
std::unordered_map<PipelineInfo, std::pair<VkPipeline, bool>, PipelineInfoHash>
|
||||
m_pipeline_objects;
|
||||
std::unordered_map<PipelineInfo, VkPipeline, PipelineInfoHash> m_pipeline_objects;
|
||||
std::unordered_map<ComputePipelineInfo, VkPipeline, ComputePipelineInfoHash>
|
||||
m_compute_pipeline_objects;
|
||||
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
|
||||
|
@ -190,45 +135,6 @@ private:
|
|||
VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE;
|
||||
VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE;
|
||||
|
||||
std::unique_ptr<VideoCommon::AsyncShaderCompiler> m_async_shader_compiler;
|
||||
|
||||
// TODO: Use templates to reduce the number of these classes.
|
||||
class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid) : m_uid(uid) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
VertexShaderUid m_uid;
|
||||
ShaderCompiler::SPIRVCodeVector m_spirv;
|
||||
VkShaderModule m_module = VK_NULL_HANDLE;
|
||||
};
|
||||
class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid) : m_uid(uid) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
PixelShaderUid m_uid;
|
||||
ShaderCompiler::SPIRVCodeVector m_spirv;
|
||||
VkShaderModule m_module = VK_NULL_HANDLE;
|
||||
};
|
||||
class PipelineCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
explicit PipelineCompilerWorkItem(const PipelineInfo& info) : m_info(info) {}
|
||||
bool Compile() override;
|
||||
void Retrieve() override;
|
||||
|
||||
private:
|
||||
PipelineInfo m_info;
|
||||
VkPipeline m_pipeline;
|
||||
};
|
||||
};
|
||||
|
||||
extern std::unique_ptr<ShaderCache> g_shader_cache;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VKPipeline.h"
|
||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
|
@ -53,12 +54,6 @@ void StateTracker::DestroyInstance()
|
|||
|
||||
bool StateTracker::Initialize()
|
||||
{
|
||||
// BBox is disabled by default.
|
||||
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
m_bbox_enabled = false;
|
||||
ClearShaders();
|
||||
|
||||
// Initialize all samplers to point by default
|
||||
for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++)
|
||||
{
|
||||
|
@ -97,122 +92,6 @@ bool StateTracker::Initialize()
|
|||
return true;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateShaderPointers()
|
||||
{
|
||||
// Clear UIDs, forcing a false match next time.
|
||||
m_vs_uid = {};
|
||||
m_gs_uid = {};
|
||||
m_ps_uid = {};
|
||||
|
||||
// Invalidate shader pointers.
|
||||
m_pipeline_state.vs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.ps = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void StateTracker::ReloadPipelineUIDCache()
|
||||
{
|
||||
class PipelineInserter final : public LinearDiskCacheReader<SerializedPipelineUID, u32>
|
||||
{
|
||||
public:
|
||||
explicit PipelineInserter(StateTracker* this_ptr_) : this_ptr(this_ptr_) {}
|
||||
void Read(const SerializedPipelineUID& key, const u32* value, u32 value_size)
|
||||
{
|
||||
this_ptr->PrecachePipelineUID(key);
|
||||
}
|
||||
|
||||
private:
|
||||
StateTracker* this_ptr;
|
||||
};
|
||||
|
||||
m_uid_cache.Sync();
|
||||
m_uid_cache.Close();
|
||||
|
||||
// UID caches don't contain any host state, so use a single uid cache per gameid.
|
||||
std::string filename = GetDiskShaderCacheFileName(APIType::Vulkan, "PipelineUID", true, false);
|
||||
if (g_ActiveConfig.bShaderCache)
|
||||
{
|
||||
PipelineInserter inserter(this);
|
||||
m_uid_cache.OpenAndRead(filename, inserter);
|
||||
}
|
||||
|
||||
// If we were using background compilation, ensure everything is ready before continuing.
|
||||
if (g_ActiveConfig.bBackgroundShaderCompiling)
|
||||
g_shader_cache->WaitForBackgroundCompilesToComplete();
|
||||
}
|
||||
|
||||
void StateTracker::AppendToPipelineUIDCache(const PipelineInfo& info)
|
||||
{
|
||||
SerializedPipelineUID sinfo;
|
||||
sinfo.blend_state_bits = info.blend_state.hex;
|
||||
sinfo.rasterizer_state_bits = info.rasterization_state.hex;
|
||||
sinfo.depth_state_bits = info.depth_state.hex;
|
||||
sinfo.vertex_decl = m_pipeline_state.vertex_format->GetVertexDeclaration();
|
||||
sinfo.vs_uid = m_vs_uid;
|
||||
sinfo.gs_uid = m_gs_uid;
|
||||
sinfo.ps_uid = m_ps_uid;
|
||||
|
||||
u32 dummy_value = 0;
|
||||
m_uid_cache.Append(sinfo, &dummy_value, 1);
|
||||
}
|
||||
|
||||
bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
|
||||
{
|
||||
PipelineInfo pinfo = {};
|
||||
|
||||
// Need to create the vertex declaration first, rather than deferring to when a game creates a
|
||||
// vertex loader that uses this format, since we need it to create a pipeline.
|
||||
pinfo.vertex_format =
|
||||
static_cast<VertexFormat*>(VertexLoaderManager::GetOrCreateMatchingFormat(uid.vertex_decl));
|
||||
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
pinfo.vs = g_shader_cache->GetVertexShaderForUid(uid.vs_uid);
|
||||
if (pinfo.vs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get vertex shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
if (g_vulkan_context->SupportsGeometryShaders() && !uid.gs_uid.GetUidData()->IsPassthrough())
|
||||
{
|
||||
pinfo.gs = g_shader_cache->GetGeometryShaderForUid(uid.gs_uid);
|
||||
if (pinfo.gs == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get geometry shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pinfo.ps = g_shader_cache->GetPixelShaderForUid(uid.ps_uid);
|
||||
if (pinfo.ps == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pixel shader from cached UID.");
|
||||
return false;
|
||||
}
|
||||
pinfo.render_pass = m_load_render_pass;
|
||||
pinfo.rasterization_state.hex = uid.rasterizer_state_bits;
|
||||
pinfo.depth_state.hex = uid.depth_state_bits;
|
||||
pinfo.blend_state.hex = uid.blend_state_bits;
|
||||
pinfo.multisampling_state.hex = m_pipeline_state.multisampling_state.hex;
|
||||
|
||||
if (g_ActiveConfig.bBackgroundShaderCompiling)
|
||||
{
|
||||
// Use async for multithreaded compilation.
|
||||
g_shader_cache->GetPipelineWithCacheResultAsync(pinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
VkPipeline pipeline = g_shader_cache->GetPipeline(pinfo);
|
||||
if (pipeline == VK_NULL_HANDLE)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Failed to get pipeline from cached UID.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to do anything with this pipeline, just make sure it exists.
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset)
|
||||
{
|
||||
if (m_vertex_buffer == buffer && m_vertex_buffer_offset == offset)
|
||||
|
@ -238,14 +117,6 @@ void StateTracker::SetRenderPass(VkRenderPass load_render_pass, VkRenderPass cle
|
|||
{
|
||||
// Should not be changed within a render pass.
|
||||
_assert_(!InRenderPass());
|
||||
|
||||
// The clear and load render passes are compatible, so we don't need to change our pipeline.
|
||||
if (m_pipeline_state.render_pass != load_render_pass)
|
||||
{
|
||||
m_pipeline_state.render_pass = load_render_pass;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
m_load_render_pass = load_render_pass;
|
||||
m_clear_render_pass = clear_render_pass;
|
||||
}
|
||||
|
@ -258,169 +129,18 @@ void StateTracker::SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& ren
|
|||
m_framebuffer_size = render_area;
|
||||
}
|
||||
|
||||
void StateTracker::SetVertexFormat(const VertexFormat* vertex_format)
|
||||
void StateTracker::SetPipeline(const VKPipeline* pipeline)
|
||||
{
|
||||
if (m_vertex_format == vertex_format)
|
||||
if (m_pipeline == pipeline)
|
||||
return;
|
||||
|
||||
m_vertex_format = vertex_format;
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
void StateTracker::SetRasterizationState(const RasterizationState& state)
|
||||
{
|
||||
if (m_pipeline_state.rasterization_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.rasterization_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetMultisamplingstate(const MultisamplingState& state)
|
||||
{
|
||||
if (m_pipeline_state.multisampling_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.multisampling_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetDepthState(const DepthState& state)
|
||||
{
|
||||
if (m_pipeline_state.depth_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.depth_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::SetBlendState(const BlendingState& state)
|
||||
{
|
||||
if (m_pipeline_state.blend_state.hex == state.hex)
|
||||
return;
|
||||
|
||||
m_pipeline_state.blend_state.hex = state.hex;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
bool StateTracker::CheckForShaderChanges()
|
||||
{
|
||||
VertexShaderUid vs_uid = GetVertexShaderUid();
|
||||
PixelShaderUid ps_uid = GetPixelShaderUid();
|
||||
ClearUnusedPixelShaderUidBits(APIType::Vulkan, &ps_uid);
|
||||
|
||||
bool changed = false;
|
||||
bool use_ubershaders = g_ActiveConfig.bDisableSpecializedShaders;
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !g_ActiveConfig.bDisableSpecializedShaders)
|
||||
{
|
||||
// Look up both VS and PS, and check if we can compile it asynchronously.
|
||||
auto vs = g_shader_cache->GetVertexShaderForUidAsync(vs_uid);
|
||||
auto ps = g_shader_cache->GetPixelShaderForUidAsync(ps_uid);
|
||||
if (vs.second || ps.second)
|
||||
{
|
||||
// One of the shaders is still pending. Use the ubershader for both.
|
||||
use_ubershaders = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the standard shaders for both.
|
||||
if (m_pipeline_state.vs != vs.first)
|
||||
{
|
||||
m_pipeline_state.vs = vs.first;
|
||||
m_vs_uid = vs_uid;
|
||||
changed = true;
|
||||
}
|
||||
if (m_pipeline_state.ps != ps.first)
|
||||
{
|
||||
m_pipeline_state.ps = ps.first;
|
||||
m_ps_uid = ps_uid;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal shader path. No ubershaders.
|
||||
if (vs_uid != m_vs_uid)
|
||||
{
|
||||
m_vs_uid = vs_uid;
|
||||
m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid);
|
||||
changed = true;
|
||||
}
|
||||
if (ps_uid != m_ps_uid)
|
||||
{
|
||||
m_ps_uid = ps_uid;
|
||||
m_pipeline_state.ps = g_shader_cache->GetPixelShaderForUid(ps_uid);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Switching to/from ubershaders? Have to adjust the vertex format and pipeline layout.
|
||||
if (use_ubershaders != m_using_ubershaders)
|
||||
{
|
||||
m_using_ubershaders = use_ubershaders;
|
||||
UpdatePipelineLayout();
|
||||
UpdatePipelineVertexFormat();
|
||||
}
|
||||
|
||||
if (use_ubershaders)
|
||||
{
|
||||
UberShader::VertexShaderUid uber_vs_uid = UberShader::GetVertexShaderUid();
|
||||
VkShaderModule vs = g_shader_cache->GetVertexUberShaderForUid(uber_vs_uid);
|
||||
if (vs != m_pipeline_state.vs)
|
||||
{
|
||||
m_uber_vs_uid = uber_vs_uid;
|
||||
m_pipeline_state.vs = vs;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
UberShader::PixelShaderUid uber_ps_uid = UberShader::GetPixelShaderUid();
|
||||
UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_ps_uid);
|
||||
VkShaderModule ps = g_shader_cache->GetPixelUberShaderForUid(uber_ps_uid);
|
||||
if (ps != m_pipeline_state.ps)
|
||||
{
|
||||
m_uber_ps_uid = uber_ps_uid;
|
||||
m_pipeline_state.ps = ps;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
GeometryShaderUid gs_uid = GetGeometryShaderUid(m_pipeline_state.rasterization_state.primitive);
|
||||
if (gs_uid != m_gs_uid)
|
||||
{
|
||||
m_gs_uid = gs_uid;
|
||||
if (gs_uid.GetUidData()->IsPassthrough())
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
else
|
||||
m_pipeline_state.gs = g_shader_cache->GetGeometryShaderForUid(gs_uid);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void StateTracker::ClearShaders()
|
||||
{
|
||||
// Set the UIDs to something that will never match, so on the first access they are checked.
|
||||
std::memset(&m_vs_uid, 0xFF, sizeof(m_vs_uid));
|
||||
std::memset(&m_gs_uid, 0xFF, sizeof(m_gs_uid));
|
||||
std::memset(&m_ps_uid, 0xFF, sizeof(m_ps_uid));
|
||||
std::memset(&m_uber_vs_uid, 0xFF, sizeof(m_uber_vs_uid));
|
||||
std::memset(&m_uber_ps_uid, 0xFF, sizeof(m_uber_ps_uid));
|
||||
|
||||
m_pipeline_state.vs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.gs = VK_NULL_HANDLE;
|
||||
m_pipeline_state.ps = VK_NULL_HANDLE;
|
||||
m_pipeline_state.vertex_format = nullptr;
|
||||
const bool new_usage =
|
||||
pipeline && (!m_pipeline || m_pipeline->GetUsage() != pipeline->GetUsage());
|
||||
|
||||
m_pipeline = pipeline;
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
if (new_usage)
|
||||
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
void StateTracker::UpdateVertexShaderConstants()
|
||||
|
@ -450,20 +170,6 @@ void StateTracker::UpdateVertexShaderConstants()
|
|||
|
||||
void StateTracker::UpdateGeometryShaderConstants()
|
||||
{
|
||||
// Skip updating geometry shader constants if it's not in use.
|
||||
if (m_pipeline_state.gs == VK_NULL_HANDLE)
|
||||
{
|
||||
// However, if the buffer has changed, we can't skip the update, because then we'll
|
||||
// try to include the now non-existant buffer in the descriptor set.
|
||||
if (m_uniform_stream_buffer->GetBuffer() ==
|
||||
m_bindings.uniform_buffer_bindings[UBO_DESCRIPTOR_SET_BINDING_GS].buffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GeometryShaderManager::dirty = true;
|
||||
}
|
||||
|
||||
if (!GeometryShaderManager::dirty || !ReserveConstantStorage())
|
||||
return;
|
||||
|
||||
|
@ -614,15 +320,6 @@ void StateTracker::SetSampler(size_t index, VkSampler sampler)
|
|||
m_dirty_flags |= DIRTY_FLAG_PS_SAMPLERS;
|
||||
}
|
||||
|
||||
void StateTracker::SetBBoxEnable(bool enable)
|
||||
{
|
||||
if (m_bbox_enabled == enable)
|
||||
return;
|
||||
|
||||
m_bbox_enabled = enable;
|
||||
UpdatePipelineLayout();
|
||||
}
|
||||
|
||||
void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range)
|
||||
{
|
||||
if (m_bindings.ps_ssbo.buffer == buffer && m_bindings.ps_ssbo.offset == offset &&
|
||||
|
@ -634,10 +331,7 @@ void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceS
|
|||
m_bindings.ps_ssbo.buffer = buffer;
|
||||
m_bindings.ps_ssbo.offset = offset;
|
||||
m_bindings.ps_ssbo.range = range;
|
||||
|
||||
// Defer descriptor update until bbox is actually enabled.
|
||||
if (IsSSBODescriptorRequired())
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
void StateTracker::UnbindTexture(VkImageView view)
|
||||
|
@ -653,10 +347,6 @@ void StateTracker::InvalidateDescriptorSets()
|
|||
{
|
||||
m_descriptor_sets.fill(VK_NULL_HANDLE);
|
||||
m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS;
|
||||
|
||||
// Defer SSBO descriptor update until bbox is actually enabled.
|
||||
if (!IsSSBODescriptorRequired())
|
||||
m_dirty_flags &= ~DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateConstants()
|
||||
|
@ -669,9 +359,8 @@ void StateTracker::InvalidateConstants()
|
|||
void StateTracker::SetPendingRebind()
|
||||
{
|
||||
m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS | DIRTY_FLAG_DESCRIPTOR_SET_BINDING |
|
||||
DIRTY_FLAG_PIPELINE_BINDING | DIRTY_FLAG_VERTEX_BUFFER |
|
||||
DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
|
||||
DIRTY_FLAG_PIPELINE;
|
||||
DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT |
|
||||
DIRTY_FLAG_SCISSOR | DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
void StateTracker::BeginRenderPass()
|
||||
|
@ -743,17 +432,14 @@ void StateTracker::SetScissor(const VkRect2D& scissor)
|
|||
|
||||
bool StateTracker::Bind(bool rebind_all /*= false*/)
|
||||
{
|
||||
// Must have a pipeline.
|
||||
if (!m_pipeline)
|
||||
return false;
|
||||
|
||||
// Check the render area if we were in a clear pass.
|
||||
if (m_current_render_pass == m_clear_render_pass && !IsViewportWithinRenderArea())
|
||||
EndRenderPass();
|
||||
|
||||
// Get new pipeline object if any parts have changed
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE && !UpdatePipeline())
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to get pipeline object, skipping draw");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a new descriptor set if any parts have changed
|
||||
if (m_dirty_flags & DIRTY_FLAG_ALL_DESCRIPTOR_SETS && !UpdateDescriptorSet())
|
||||
{
|
||||
|
@ -780,20 +466,20 @@ bool StateTracker::Bind(bool rebind_all /*= false*/)
|
|||
if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER || rebind_all)
|
||||
vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE_BINDING || rebind_all)
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_object);
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE || rebind_all)
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline());
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SET_BINDING || rebind_all)
|
||||
{
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_pipeline_state.pipeline_layout, 0, m_num_active_descriptor_sets,
|
||||
m_pipeline->GetVkPipelineLayout(), 0, m_num_active_descriptor_sets,
|
||||
m_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS,
|
||||
m_bindings.uniform_buffer_offsets.data());
|
||||
}
|
||||
else if (m_dirty_flags & DIRTY_FLAG_DYNAMIC_OFFSETS)
|
||||
{
|
||||
vkCmdBindDescriptorSets(
|
||||
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout,
|
||||
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(),
|
||||
DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1,
|
||||
&m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS],
|
||||
NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data());
|
||||
|
@ -933,105 +619,6 @@ void StateTracker::EndClearRenderPass()
|
|||
EndRenderPass();
|
||||
}
|
||||
|
||||
VkPipeline StateTracker::GetPipelineAndCacheUID()
|
||||
{
|
||||
// We can't cache ubershader uids, only normal shader uids.
|
||||
if (g_ActiveConfig.CanBackgroundCompileShaders() && !m_using_ubershaders)
|
||||
{
|
||||
// Append to UID cache if it is a new pipeline.
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResultAsync(m_pipeline_state);
|
||||
if (!result.second && g_ActiveConfig.bShaderCache)
|
||||
AppendToPipelineUIDCache(m_pipeline_state);
|
||||
|
||||
// Still waiting for the pipeline to compile?
|
||||
if (!result.first.second)
|
||||
return result.first.first;
|
||||
|
||||
// Use ubershader instead.
|
||||
m_using_ubershaders = true;
|
||||
UpdatePipelineLayout();
|
||||
UpdatePipelineVertexFormat();
|
||||
|
||||
PipelineInfo uber_info = m_pipeline_state;
|
||||
UberShader::VertexShaderUid uber_vuid = UberShader::GetVertexShaderUid();
|
||||
UberShader::PixelShaderUid uber_puid = UberShader::GetPixelShaderUid();
|
||||
UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_puid);
|
||||
uber_info.vs = g_shader_cache->GetVertexUberShaderForUid(uber_vuid);
|
||||
uber_info.ps = g_shader_cache->GetPixelUberShaderForUid(uber_puid);
|
||||
|
||||
auto uber_result = g_shader_cache->GetPipelineWithCacheResult(uber_info);
|
||||
return uber_result.first;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to the UID cache if it is a new pipeline.
|
||||
auto result = g_shader_cache->GetPipelineWithCacheResult(m_pipeline_state);
|
||||
if (!result.second && !m_using_ubershaders && g_ActiveConfig.bShaderCache)
|
||||
AppendToPipelineUIDCache(m_pipeline_state);
|
||||
|
||||
return result.first;
|
||||
}
|
||||
}
|
||||
|
||||
bool StateTracker::IsSSBODescriptorRequired() const
|
||||
{
|
||||
return m_bbox_enabled || (m_using_ubershaders && g_ActiveConfig.bBBoxEnable &&
|
||||
g_ActiveConfig.BBoxUseFragmentShaderImplementation());
|
||||
}
|
||||
|
||||
bool StateTracker::UpdatePipeline()
|
||||
{
|
||||
// We need at least a vertex and fragment shader
|
||||
if (m_pipeline_state.vs == VK_NULL_HANDLE || m_pipeline_state.ps == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
// Grab a new pipeline object, this can fail.
|
||||
m_pipeline_object = GetPipelineAndCacheUID();
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE_BINDING;
|
||||
return m_pipeline_object != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void StateTracker::UpdatePipelineLayout()
|
||||
{
|
||||
const bool use_bbox_pipeline_layout = IsSSBODescriptorRequired();
|
||||
VkPipelineLayout pipeline_layout =
|
||||
use_bbox_pipeline_layout ? g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||
if (m_pipeline_state.pipeline_layout == pipeline_layout)
|
||||
return;
|
||||
|
||||
// Change the number of active descriptor sets, as well as the pipeline layout
|
||||
m_pipeline_state.pipeline_layout = pipeline_layout;
|
||||
if (use_bbox_pipeline_layout)
|
||||
{
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS;
|
||||
|
||||
// The bbox buffer never changes, so we defer descriptor updates until it is enabled.
|
||||
if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)
|
||||
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
}
|
||||
|
||||
void StateTracker::UpdatePipelineVertexFormat()
|
||||
{
|
||||
const NativeVertexFormat* vertex_format =
|
||||
m_using_ubershaders ?
|
||||
VertexLoaderManager::GetUberVertexFormat(m_vertex_format->GetVertexDeclaration()) :
|
||||
m_vertex_format;
|
||||
if (m_pipeline_state.vertex_format == vertex_format)
|
||||
return;
|
||||
|
||||
m_pipeline_state.vertex_format = static_cast<const VertexFormat*>(vertex_format);
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateDescriptorSet()
|
||||
{
|
||||
const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO
|
||||
|
@ -1051,6 +638,9 @@ bool StateTracker::UpdateDescriptorSet()
|
|||
|
||||
for (size_t i = 0; i < NUM_UBO_DESCRIPTOR_SET_BINDINGS; i++)
|
||||
{
|
||||
if (i == UBO_DESCRIPTOR_SET_BINDING_GS && !g_vulkan_context->SupportsGeometryShaders())
|
||||
continue;
|
||||
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
|
@ -1091,7 +681,7 @@ bool StateTracker::UpdateDescriptorSet()
|
|||
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||
}
|
||||
|
||||
if (IsSSBODescriptorRequired() &&
|
||||
if (g_vulkan_context->SupportsBoundingBox() &&
|
||||
(m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
|
||||
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE))
|
||||
{
|
||||
|
@ -1119,6 +709,7 @@ bool StateTracker::UpdateDescriptorSet()
|
|||
if (num_writes > 0)
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr);
|
||||
|
||||
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,19 +9,14 @@
|
|||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/LinearDiskCache.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCache.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/NativeVertexFormat.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class VKPipeline;
|
||||
class StreamBuffer;
|
||||
class VertexFormat;
|
||||
|
||||
|
@ -35,31 +30,18 @@ public:
|
|||
static bool CreateInstance();
|
||||
static void DestroyInstance();
|
||||
|
||||
const RasterizationState& GetRasterizationState() const
|
||||
{
|
||||
return m_pipeline_state.rasterization_state;
|
||||
}
|
||||
const DepthState& GetDepthStencilState() const { return m_pipeline_state.depth_state; }
|
||||
const BlendingState& GetBlendState() const { return m_pipeline_state.blend_state; }
|
||||
const std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS>& GetPSSamplerBindings() const
|
||||
{
|
||||
return m_bindings.ps_samplers;
|
||||
}
|
||||
VkFramebuffer GetFramebuffer() const { return m_framebuffer; }
|
||||
const VKPipeline* GetPipeline() const { return m_pipeline; }
|
||||
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
|
||||
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
|
||||
|
||||
void SetRenderPass(VkRenderPass load_render_pass, VkRenderPass clear_render_pass);
|
||||
void SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& render_area);
|
||||
void SetVertexFormat(const VertexFormat* vertex_format);
|
||||
|
||||
void SetRasterizationState(const RasterizationState& state);
|
||||
void SetMultisamplingstate(const MultisamplingState& state);
|
||||
void SetDepthState(const DepthState& state);
|
||||
void SetBlendState(const BlendingState& state);
|
||||
|
||||
bool CheckForShaderChanges();
|
||||
void ClearShaders();
|
||||
void SetPipeline(const VKPipeline* pipeline);
|
||||
|
||||
void UpdateVertexShaderConstants();
|
||||
void UpdateGeometryShaderConstants();
|
||||
|
@ -68,7 +50,6 @@ public:
|
|||
void SetTexture(size_t index, VkImageView view);
|
||||
void SetSampler(size_t index, VkSampler sampler);
|
||||
|
||||
void SetBBoxEnable(bool enable);
|
||||
void SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range);
|
||||
|
||||
void UnbindTexture(VkImageView view);
|
||||
|
@ -117,30 +98,11 @@ public:
|
|||
|
||||
bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const;
|
||||
|
||||
// Reloads the UID cache, ensuring all pipelines used by the game so far have been created.
|
||||
void ReloadPipelineUIDCache();
|
||||
|
||||
// Clears shader pointers, ensuring that now-deleted modules are not used.
|
||||
void InvalidateShaderPointers();
|
||||
|
||||
private:
|
||||
// Serialized version of PipelineInfo, used when loading/saving the pipeline UID cache.
|
||||
struct SerializedPipelineUID
|
||||
{
|
||||
u32 rasterizer_state_bits;
|
||||
u32 depth_state_bits;
|
||||
u32 blend_state_bits;
|
||||
PortableVertexDeclaration vertex_decl;
|
||||
VertexShaderUid vs_uid;
|
||||
GeometryShaderUid gs_uid;
|
||||
PixelShaderUid ps_uid;
|
||||
};
|
||||
|
||||
// Number of descriptor sets for game draws.
|
||||
enum
|
||||
{
|
||||
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1,
|
||||
NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
|
||||
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
|
||||
};
|
||||
|
||||
enum DITRY_FLAG : u32
|
||||
|
@ -157,7 +119,6 @@ private:
|
|||
DIRTY_FLAG_SCISSOR = (1 << 9),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 10),
|
||||
DIRTY_FLAG_DESCRIPTOR_SET_BINDING = (1 << 11),
|
||||
DIRTY_FLAG_PIPELINE_BINDING = (1 << 12),
|
||||
|
||||
DIRTY_FLAG_ALL_DESCRIPTOR_SETS = DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO |
|
||||
DIRTY_FLAG_PS_SAMPLERS | DIRTY_FLAG_PS_SSBO
|
||||
|
@ -165,28 +126,10 @@ private:
|
|||
|
||||
bool Initialize();
|
||||
|
||||
// Appends the specified pipeline info, combined with the UIDs stored in the class.
|
||||
// The info is here so that we can store variations of a UID, e.g. blend state.
|
||||
void AppendToPipelineUIDCache(const PipelineInfo& info);
|
||||
|
||||
// Precaches a pipeline based on the UID information.
|
||||
bool PrecachePipelineUID(const SerializedPipelineUID& uid);
|
||||
|
||||
// Check that the specified viewport is within the render area.
|
||||
// If not, ends the render pass if it is a clear render pass.
|
||||
bool IsViewportWithinRenderArea() const;
|
||||
|
||||
// Obtains a Vulkan pipeline object for the specified pipeline configuration.
|
||||
// Also adds this pipeline configuration to the UID cache if it is not present already.
|
||||
VkPipeline GetPipelineAndCacheUID();
|
||||
|
||||
// Are bounding box ubershaders enabled? If so, we need to ensure the SSBO is set up,
|
||||
// since the bbox writes are determined by a uniform.
|
||||
bool IsSSBODescriptorRequired() const;
|
||||
|
||||
bool UpdatePipeline();
|
||||
void UpdatePipelineLayout();
|
||||
void UpdatePipelineVertexFormat();
|
||||
bool UpdateDescriptorSet();
|
||||
|
||||
// Allocates storage in the uniform buffer of the specified size. If this storage cannot be
|
||||
|
@ -205,18 +148,8 @@ private:
|
|||
VkDeviceSize m_index_buffer_offset = 0;
|
||||
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
|
||||
|
||||
// shader state
|
||||
VertexShaderUid m_vs_uid = {};
|
||||
GeometryShaderUid m_gs_uid = {};
|
||||
PixelShaderUid m_ps_uid = {};
|
||||
UberShader::VertexShaderUid m_uber_vs_uid = {};
|
||||
UberShader::PixelShaderUid m_uber_ps_uid = {};
|
||||
bool m_using_ubershaders = false;
|
||||
|
||||
// pipeline state
|
||||
PipelineInfo m_pipeline_state = {};
|
||||
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
|
||||
const VertexFormat* m_vertex_format = nullptr;
|
||||
const VKPipeline* m_pipeline = nullptr;
|
||||
|
||||
// shader bindings
|
||||
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {};
|
||||
|
@ -230,8 +163,8 @@ private:
|
|||
|
||||
VkDescriptorBufferInfo ps_ssbo = {};
|
||||
} m_bindings;
|
||||
u32 m_num_active_descriptor_sets = 0;
|
||||
size_t m_uniform_buffer_reserve_size = 0;
|
||||
u32 m_num_active_descriptor_sets = 0;
|
||||
|
||||
// rasterization
|
||||
VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
|
@ -246,18 +179,11 @@ private:
|
|||
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
|
||||
VkRect2D m_framebuffer_size = {};
|
||||
VkRect2D m_framebuffer_render_area = {};
|
||||
bool m_bbox_enabled = false;
|
||||
|
||||
// CPU access tracking
|
||||
u32 m_draw_counter = 0;
|
||||
std::vector<u32> m_cpu_accesses_this_frame;
|
||||
std::vector<u32> m_scheduled_command_buffer_kicks;
|
||||
bool m_allow_background_execution = true;
|
||||
|
||||
// Draw state cache on disk
|
||||
// We don't actually use the value field here, instead we generate the shaders from the uid
|
||||
// on-demand. If all goes well, it should hit the shader and Vulkan pipeline cache, therefore
|
||||
// loading should be reasonably efficient.
|
||||
LinearDiskCache<SerializedPipelineUID, u32> m_uid_cache;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -592,16 +592,19 @@ void UtilityShaderDraw::BindDescriptors()
|
|||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
|
||||
set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
UBO_DESCRIPTOR_SET_BINDING_GS,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
if (g_vulkan_context->SupportsGeometryShaders())
|
||||
{
|
||||
set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
set,
|
||||
UBO_DESCRIPTOR_SET_BINDING_GS,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&dummy_uniform_buffer,
|
||||
nullptr};
|
||||
}
|
||||
|
||||
set_writes[num_set_writes++] = {
|
||||
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, UBO_DESCRIPTOR_SET_BINDING_PS, 0, 1,
|
||||
|
|
|
@ -139,8 +139,6 @@ void VertexManager::vFlush()
|
|||
u32 index_count = IndexGenerator::GetIndexLen();
|
||||
|
||||
// Update tracked state
|
||||
StateTracker::GetInstance()->SetVertexFormat(vertex_format);
|
||||
StateTracker::GetInstance()->CheckForShaderChanges();
|
||||
StateTracker::GetInstance()->UpdateVertexShaderConstants();
|
||||
StateTracker::GetInstance()->UpdateGeometryShaderConstants();
|
||||
StateTracker::GetInstance()->UpdatePixelShaderConstants();
|
||||
|
@ -165,12 +163,10 @@ void VertexManager::vFlush()
|
|||
bounding_box->Flush();
|
||||
bounding_box->Invalidate();
|
||||
}
|
||||
|
||||
// Update which descriptor set/pipeline layout to use.
|
||||
StateTracker::GetInstance()->SetBBoxEnable(bounding_box_enabled);
|
||||
}
|
||||
|
||||
// Bind all pending state to the command buffer
|
||||
g_renderer->SetPipeline(m_current_pipeline_object);
|
||||
if (!StateTracker::GetInstance()->Bind())
|
||||
{
|
||||
WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count);
|
||||
|
|
|
@ -223,6 +223,7 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
::g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
|
||||
// Invoke init methods on main wrapper classes.
|
||||
|
@ -230,21 +231,14 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
// for the remaining classes may call methods on these.
|
||||
if (!StateTracker::CreateInstance() || !FramebufferManager::GetInstance()->Initialize() ||
|
||||
!Renderer::GetInstance()->Initialize() || !VertexManager::GetInstance()->Initialize() ||
|
||||
!TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize())
|
||||
!TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize() ||
|
||||
!::g_shader_cache->Initialize())
|
||||
{
|
||||
PanicAlert("Failed to initialize Vulkan classes.");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure all pipelines previously used by the game have been created.
|
||||
StateTracker::GetInstance()->ReloadPipelineUIDCache();
|
||||
|
||||
// Lastly, precompile ubershaders, if requested.
|
||||
// This has to be done after the texture cache and shader cache are initialized.
|
||||
if (g_ActiveConfig.CanPrecompileUberShaders())
|
||||
g_shader_cache->PrecompileUberShaders();
|
||||
|
||||
// Display the name so the user knows which device was actually created.
|
||||
INFO_LOG(VIDEO, "Vulkan Device: %s", g_vulkan_context->GetDeviceProperties().deviceName);
|
||||
return true;
|
||||
|
@ -252,13 +246,17 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
|
||||
void VideoBackend::Shutdown()
|
||||
{
|
||||
if (g_renderer)
|
||||
g_renderer->Shutdown();
|
||||
|
||||
if (g_command_buffer_mgr)
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
if (::g_shader_cache)
|
||||
::g_shader_cache->Shutdown();
|
||||
|
||||
if (g_renderer)
|
||||
g_renderer->Shutdown();
|
||||
|
||||
g_perf_query.reset();
|
||||
::g_shader_cache.reset();
|
||||
g_texture_cache.reset();
|
||||
g_vertex_manager.reset();
|
||||
g_renderer.reset();
|
||||
|
|
|
@ -31,6 +31,7 @@ void SetGenerationMode()
|
|||
RasterizationState state = {};
|
||||
state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType());
|
||||
g_renderer->SetRasterizationState(state);
|
||||
g_vertex_manager->SetRasterizationStateChanged();
|
||||
}
|
||||
|
||||
void SetScissor()
|
||||
|
@ -132,6 +133,7 @@ void SetDepthMode()
|
|||
DepthState state = {};
|
||||
state.Generate(bpmem);
|
||||
g_renderer->SetDepthState(state);
|
||||
g_vertex_manager->SetDepthStateChanged();
|
||||
}
|
||||
|
||||
void SetBlendMode()
|
||||
|
@ -139,6 +141,7 @@ void SetBlendMode()
|
|||
BlendingState state = {};
|
||||
state.Generate(bpmem);
|
||||
g_renderer->SetBlendingState(state);
|
||||
g_vertex_manager->SetBlendingStateChanged();
|
||||
}
|
||||
|
||||
/* Explanation of the magic behind ClearScreen:
|
||||
|
|
|
@ -32,6 +32,7 @@ set(SRCS
|
|||
PostProcessing.cpp
|
||||
RenderBase.cpp
|
||||
RenderState.cpp
|
||||
ShaderCache.cpp
|
||||
ShaderGenCommon.cpp
|
||||
Statistics.cpp
|
||||
UberShaderCommon.cpp
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/ShaderGenCommon.h"
|
||||
#include "VideoCommon/VertexManagerBase.h"
|
||||
|
||||
enum class APIType;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/PixelShaderManager.h"
|
||||
#include "VideoCommon/PostProcessing.h"
|
||||
#include "VideoCommon/ShaderCache.h"
|
||||
#include "VideoCommon/ShaderGenCommon.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
|
@ -92,6 +93,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
|
|||
|
||||
m_surface_handle = Host_GetRenderHandle();
|
||||
m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits;
|
||||
m_last_efb_multisamples = g_ActiveConfig.iMultisamples;
|
||||
}
|
||||
|
||||
Renderer::~Renderer() = default;
|
||||
|
@ -234,11 +236,20 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet
|
|||
bool Renderer::CheckForHostConfigChanges()
|
||||
{
|
||||
ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent();
|
||||
if (new_host_config.bits == m_last_host_config_bits)
|
||||
if (new_host_config.bits == m_last_host_config_bits &&
|
||||
m_last_efb_multisamples == g_ActiveConfig.iMultisamples)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
|
||||
m_last_host_config_bits = new_host_config.bits;
|
||||
m_last_efb_multisamples = g_ActiveConfig.iMultisamples;
|
||||
|
||||
// Reload shaders.
|
||||
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
|
||||
SetPipeline(nullptr);
|
||||
g_vertex_manager->InvalidatePipelineObject();
|
||||
g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -688,6 +699,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
// Set default viewport and scissor, for the clear to work correctly
|
||||
// New frame
|
||||
stats.ResetFrame();
|
||||
g_shader_cache->RetrieveAsyncShaders();
|
||||
|
||||
// We invalidate the pipeline object at the start of the frame.
|
||||
// This is for the rare case where only a single pipeline configuration is used,
|
||||
// and hybrid ubershaders have compiled the specialized shader, but without any
|
||||
// state changes the specialized shader will not take over.
|
||||
g_vertex_manager->InvalidatePipelineObject();
|
||||
|
||||
Core::Callback_VideoCopiedToXFB(true);
|
||||
}
|
||||
|
|
|
@ -243,6 +243,7 @@ protected:
|
|||
std::mutex m_swap_mutex;
|
||||
|
||||
u32 m_last_host_config_bits = 0;
|
||||
u32 m_last_efb_multisamples = 1;
|
||||
|
||||
private:
|
||||
void RunFrameDumps();
|
||||
|
|
|
@ -0,0 +1,913 @@
|
|||
// 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 = std::make_unique<VideoCommon::AsyncShaderCompiler>();
|
||||
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.CanPrecompileUberShaders())
|
||||
PrecompileUberShaders();
|
||||
|
||||
// Compile all known UIDs.
|
||||
CompileMissingPipelines();
|
||||
|
||||
// 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()
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
|
||||
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();
|
||||
m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads());
|
||||
}
|
||||
|
||||
void ShaderCache::RetrieveAsyncShaders()
|
||||
{
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
}
|
||||
|
||||
void ShaderCache::Shutdown()
|
||||
{
|
||||
m_async_shader_compiler->StopWorkerThreads();
|
||||
m_async_shader_compiler->RetrieveWorkItems();
|
||||
|
||||
ClearShaderCaches();
|
||||
ClearPipelineCaches();
|
||||
}
|
||||
|
||||
const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& 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 GXPipelineConfig& 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 GXUberPipelineConfig& 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(const std::string& msg)
|
||||
{
|
||||
m_async_shader_compiler->WaitUntilCompletion([&msg](size_t completed, size_t total) {
|
||||
Host_UpdateProgressDialog(msg.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)
|
||||
{
|
||||
GXPipelineConfig 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);
|
||||
}
|
||||
|
||||
WaitForAsyncCompiler(GetStringT("Compiling shaders..."));
|
||||
}
|
||||
|
||||
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 GXPipelineConfig& 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 GXUberPipelineConfig& 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 GXPipelineConfig& 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 GXUberPipelineConfig& 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 GXPipelineConfig& 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;
|
||||
}
|
||||
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
virtual 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;
|
||||
}
|
||||
|
||||
virtual 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 GXPipelineConfig& uid)
|
||||
{
|
||||
class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineConfig& uid_,
|
||||
const AbstractPipelineConfig& config_)
|
||||
: shader_cache(shader_cache_), uid(uid_), config(config_)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compile() override
|
||||
{
|
||||
pipeline = g_renderer->CreatePipeline(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); }
|
||||
private:
|
||||
ShaderCache* shader_cache;
|
||||
std::unique_ptr<AbstractPipeline> pipeline;
|
||||
GXPipelineConfig 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 GXUberPipelineConfig& uid)
|
||||
{
|
||||
class UberPipelineWorkItem final : public AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
UberPipelineWorkItem(ShaderCache* shader_cache_, const GXUberPipelineConfig& uid_,
|
||||
const AbstractPipelineConfig& config_)
|
||||
: shader_cache(shader_cache_), uid(uid_), config(config_)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compile() override
|
||||
{
|
||||
pipeline = g_renderer->CreatePipeline(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Retrieve() override
|
||||
{
|
||||
shader_cache->InsertGXUberPipeline(uid, std::move(pipeline));
|
||||
}
|
||||
|
||||
private:
|
||||
ShaderCache* shader_cache;
|
||||
std::unique_ptr<AbstractPipeline> pipeline;
|
||||
GXUberPipelineConfig uid;
|
||||
AbstractPipelineConfig config;
|
||||
};
|
||||
|
||||
auto config = GetGXUberPipelineConfig(uid);
|
||||
if (!config)
|
||||
{
|
||||
// One or more stages failed to compile.
|
||||
InsertGXUberPipeline(uid, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto wi = m_async_shader_compiler->CreateWorkItem<UberPipelineWorkItem>(this, uid, *config);
|
||||
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);
|
||||
});
|
||||
|
||||
// Wait for shaders to finish compiling.
|
||||
WaitForAsyncCompiler(GetStringT("Compiling uber shaders..."));
|
||||
|
||||
// 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) {
|
||||
GXUberPipelineConfig 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
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#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/NativeVertexFormat.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/UberShaderPixel.h"
|
||||
#include "VideoCommon/UberShaderVertex.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
|
||||
class NativeVertexFormat;
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
struct GXPipelineConfig
|
||||
{
|
||||
const NativeVertexFormat* vertex_format;
|
||||
VertexShaderUid vs_uid;
|
||||
GeometryShaderUid gs_uid;
|
||||
PixelShaderUid ps_uid;
|
||||
RasterizationState rasterization_state;
|
||||
DepthState depth_state;
|
||||
BlendingState blending_state;
|
||||
|
||||
bool operator<(const GXPipelineConfig& rhs) const
|
||||
{
|
||||
return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex,
|
||||
blending_state.hex) < std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid,
|
||||
rhs.ps_uid, rhs.rasterization_state.hex,
|
||||
rhs.depth_state.hex, rhs.blending_state.hex);
|
||||
}
|
||||
bool operator==(const GXPipelineConfig& rhs) const
|
||||
{
|
||||
return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex,
|
||||
blending_state.hex) == std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid,
|
||||
rhs.ps_uid, rhs.rasterization_state.hex,
|
||||
rhs.depth_state.hex, rhs.blending_state.hex);
|
||||
}
|
||||
bool operator!=(const GXPipelineConfig& rhs) const { return !operator==(rhs); }
|
||||
};
|
||||
struct GXUberPipelineConfig
|
||||
{
|
||||
const NativeVertexFormat* vertex_format;
|
||||
UberShader::VertexShaderUid vs_uid;
|
||||
GeometryShaderUid gs_uid;
|
||||
UberShader::PixelShaderUid ps_uid;
|
||||
RasterizationState rasterization_state;
|
||||
DepthState depth_state;
|
||||
BlendingState blending_state;
|
||||
|
||||
bool operator<(const GXUberPipelineConfig& rhs) const
|
||||
{
|
||||
return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex,
|
||||
blending_state.hex) < std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid,
|
||||
rhs.ps_uid, rhs.rasterization_state.hex,
|
||||
rhs.depth_state.hex, rhs.blending_state.hex);
|
||||
}
|
||||
bool operator==(const GXUberPipelineConfig& rhs) const
|
||||
{
|
||||
return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex,
|
||||
blending_state.hex) == std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid,
|
||||
rhs.ps_uid, rhs.rasterization_state.hex,
|
||||
rhs.depth_state.hex, rhs.blending_state.hex);
|
||||
}
|
||||
bool operator!=(const GXUberPipelineConfig& rhs) const { return !operator==(rhs); }
|
||||
};
|
||||
|
||||
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 GXPipelineConfig& uid);
|
||||
const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineConfig& uid);
|
||||
|
||||
// Accesses ShaderGen shader caches asynchronously.
|
||||
// The optional will be empty if this pipeline is now background compiling.
|
||||
std::optional<const AbstractPipeline*> GetPipelineForUidAsync(const GXPipelineConfig& uid);
|
||||
|
||||
private:
|
||||
void WaitForAsyncCompiler(const std::string& msg);
|
||||
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 GXPipelineConfig& uid);
|
||||
std::optional<AbstractPipelineConfig> GetGXUberPipelineConfig(const GXUberPipelineConfig& uid);
|
||||
const AbstractPipeline* InsertGXPipeline(const GXPipelineConfig& config,
|
||||
std::unique_ptr<AbstractPipeline> pipeline);
|
||||
const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineConfig& config,
|
||||
std::unique_ptr<AbstractPipeline> pipeline);
|
||||
void AppendGXPipelineUID(const GXPipelineConfig& 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 GXPipelineConfig& uid);
|
||||
void QueueUberPipelineCompile(const GXUberPipelineConfig& 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
|
||||
// TODO: Use unordered_map for speed.
|
||||
std::map<GXPipelineConfig, std::pair<std::unique_ptr<AbstractPipeline>, bool>>
|
||||
m_gx_pipeline_cache;
|
||||
std::map<GXUberPipelineConfig, 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;
|
|
@ -37,28 +37,31 @@ ShaderHostConfig ShaderHostConfig::GetCurrent()
|
|||
}
|
||||
|
||||
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
|
||||
bool include_host_config)
|
||||
bool include_host_config, bool include_api)
|
||||
{
|
||||
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
|
||||
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
|
||||
|
||||
std::string filename = File::GetUserPath(D_SHADERCACHE_IDX);
|
||||
switch (api_type)
|
||||
if (include_api)
|
||||
{
|
||||
case APIType::D3D:
|
||||
filename += "D3D";
|
||||
break;
|
||||
case APIType::OpenGL:
|
||||
filename += "OpenGL";
|
||||
break;
|
||||
case APIType::Vulkan:
|
||||
filename += "Vulkan";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
switch (api_type)
|
||||
{
|
||||
case APIType::D3D:
|
||||
filename += "D3D";
|
||||
break;
|
||||
case APIType::OpenGL:
|
||||
filename += "OpenGL";
|
||||
break;
|
||||
case APIType::Vulkan:
|
||||
filename += "Vulkan";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
filename += '-';
|
||||
}
|
||||
|
||||
filename += '-';
|
||||
filename += type;
|
||||
|
||||
if (include_gameid)
|
||||
|
|
|
@ -187,7 +187,7 @@ union ShaderHostConfig
|
|||
|
||||
// Gets the filename of the specified type of cache object (e.g. vertex shader, pipeline).
|
||||
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
|
||||
bool include_host_config);
|
||||
bool include_host_config, bool include_api = true);
|
||||
|
||||
template <class T>
|
||||
inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type,
|
||||
|
|
|
@ -105,10 +105,11 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count,
|
|||
Flush();
|
||||
|
||||
// Have to update the rasterization state for point/line cull modes.
|
||||
m_current_primitive_type = new_primitive_type;
|
||||
RasterizationState raster_state = {};
|
||||
raster_state.Generate(bpmem, new_primitive_type);
|
||||
g_renderer->SetRasterizationState(raster_state);
|
||||
m_current_primitive_type = new_primitive_type;
|
||||
SetRasterizationStateChanged();
|
||||
}
|
||||
|
||||
// Check for size in buffer, if the buffer gets full, call Flush()
|
||||
|
@ -386,6 +387,10 @@ void VertexManagerBase::Flush()
|
|||
|
||||
if (!m_cull_all)
|
||||
{
|
||||
// Update the pipeline, or compile one if needed.
|
||||
UpdatePipelineConfig();
|
||||
UpdatePipelineObject();
|
||||
|
||||
// set the rest of the global constants
|
||||
GeometryShaderManager::SetConstants();
|
||||
PixelShaderManager::SetConstants();
|
||||
|
@ -477,3 +482,114 @@ void VertexManagerBase::CalculateZSlope(NativeVertexFormat* format)
|
|||
m_zslope.f0 = out[2] - (out[0] * m_zslope.dfdx + out[1] * m_zslope.dfdy);
|
||||
m_zslope.dirty = true;
|
||||
}
|
||||
|
||||
void VertexManagerBase::UpdatePipelineConfig()
|
||||
{
|
||||
NativeVertexFormat* vertex_format = VertexLoaderManager::GetCurrentVertexFormat();
|
||||
if (vertex_format != m_current_pipeline_config.vertex_format)
|
||||
{
|
||||
m_current_pipeline_config.vertex_format = vertex_format;
|
||||
m_current_uber_pipeline_config.vertex_format =
|
||||
VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration());
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
|
||||
VertexShaderUid vs_uid = GetVertexShaderUid();
|
||||
if (vs_uid != m_current_pipeline_config.vs_uid)
|
||||
{
|
||||
m_current_pipeline_config.vs_uid = vs_uid;
|
||||
m_current_uber_pipeline_config.vs_uid = UberShader::GetVertexShaderUid();
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
|
||||
PixelShaderUid ps_uid = GetPixelShaderUid();
|
||||
if (ps_uid != m_current_pipeline_config.ps_uid)
|
||||
{
|
||||
m_current_pipeline_config.ps_uid = ps_uid;
|
||||
m_current_uber_pipeline_config.ps_uid = UberShader::GetPixelShaderUid();
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
|
||||
GeometryShaderUid gs_uid = GetGeometryShaderUid(GetCurrentPrimitiveType());
|
||||
if (gs_uid != m_current_pipeline_config.gs_uid)
|
||||
{
|
||||
m_current_pipeline_config.gs_uid = gs_uid;
|
||||
m_current_uber_pipeline_config.gs_uid = gs_uid;
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
|
||||
if (m_rasterization_state_changed)
|
||||
{
|
||||
m_rasterization_state_changed = false;
|
||||
|
||||
RasterizationState new_rs = {};
|
||||
new_rs.Generate(bpmem, m_current_primitive_type);
|
||||
if (new_rs != m_current_pipeline_config.rasterization_state)
|
||||
{
|
||||
m_current_pipeline_config.rasterization_state = new_rs;
|
||||
m_current_uber_pipeline_config.rasterization_state = new_rs;
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_depth_state_changed)
|
||||
{
|
||||
m_depth_state_changed = false;
|
||||
|
||||
DepthState new_ds = {};
|
||||
new_ds.Generate(bpmem);
|
||||
if (new_ds != m_current_pipeline_config.depth_state)
|
||||
{
|
||||
m_current_pipeline_config.depth_state = new_ds;
|
||||
m_current_uber_pipeline_config.depth_state = new_ds;
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_blending_state_changed)
|
||||
{
|
||||
m_blending_state_changed = false;
|
||||
|
||||
BlendingState new_bs = {};
|
||||
new_bs.Generate(bpmem);
|
||||
if (new_bs != m_current_pipeline_config.blending_state)
|
||||
{
|
||||
m_current_pipeline_config.blending_state = new_bs;
|
||||
m_current_uber_pipeline_config.blending_state = new_bs;
|
||||
m_pipeline_config_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VertexManagerBase::UpdatePipelineObject()
|
||||
{
|
||||
if (!m_pipeline_config_changed)
|
||||
return;
|
||||
|
||||
m_current_pipeline_object = nullptr;
|
||||
m_pipeline_config_changed = false;
|
||||
|
||||
// Try for specialized shaders.
|
||||
if (!g_ActiveConfig.bDisableSpecializedShaders)
|
||||
{
|
||||
// Can we background compile shaders? If so, get the pipeline asynchronously.
|
||||
if (g_ActiveConfig.bBackgroundShaderCompiling)
|
||||
{
|
||||
auto res = g_shader_cache->GetPipelineForUidAsync(m_current_pipeline_config);
|
||||
if (res)
|
||||
{
|
||||
// Specialized shaders are ready.
|
||||
m_current_pipeline_object = *res;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to ubershaders.
|
||||
m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/ShaderCache.h"
|
||||
|
||||
class DataReader;
|
||||
class NativeVertexFormat;
|
||||
|
@ -58,6 +59,16 @@ public:
|
|||
|
||||
std::pair<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:
|
||||
virtual void vDoState(PointerWrap& p) {}
|
||||
virtual void ResetBuffer(u32 stride) = 0;
|
||||
|
@ -72,8 +83,15 @@ protected:
|
|||
Slope m_zslope = {};
|
||||
void CalculateZSlope(NativeVertexFormat* format);
|
||||
|
||||
bool m_cull_all = false;
|
||||
VideoCommon::GXPipelineConfig m_current_pipeline_config = {};
|
||||
VideoCommon::GXUberPipelineConfig m_current_uber_pipeline_config = {};
|
||||
const AbstractPipeline* m_current_pipeline_object = nullptr;
|
||||
PrimitiveType m_current_primitive_type = PrimitiveType::Points;
|
||||
bool m_pipeline_config_changed = true;
|
||||
bool m_rasterization_state_changed = true;
|
||||
bool m_depth_state_changed = true;
|
||||
bool m_blending_state_changed = true;
|
||||
bool m_cull_all = false;
|
||||
|
||||
private:
|
||||
bool m_is_flushed = true;
|
||||
|
@ -84,6 +102,8 @@ private:
|
|||
|
||||
virtual void CreateDeviceObjects() {}
|
||||
virtual void DestroyDeviceObjects() {}
|
||||
void UpdatePipelineConfig();
|
||||
void UpdatePipelineObject();
|
||||
};
|
||||
|
||||
extern std::unique_ptr<VertexManagerBase> g_vertex_manager;
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
<ClCompile Include="RenderBase.cpp" />
|
||||
<ClCompile Include="RenderState.cpp" />
|
||||
<ClCompile Include="LightingShaderGen.cpp" />
|
||||
<ClCompile Include="ShaderCache.cpp" />
|
||||
<ClCompile Include="ShaderGenCommon.cpp" />
|
||||
<ClCompile Include="UberShaderCommon.cpp" />
|
||||
<ClCompile Include="UberShaderPixel.cpp" />
|
||||
|
@ -119,6 +120,7 @@
|
|||
<ClInclude Include="Fifo.h" />
|
||||
<ClInclude Include="FPSCounter.h" />
|
||||
<ClInclude Include="FramebufferManagerBase.h" />
|
||||
<ClInclude Include="ShaderCache.h" />
|
||||
<ClInclude Include="UberShaderCommon.h" />
|
||||
<ClInclude Include="UberShaderPixel.h" />
|
||||
<ClInclude Include="HiresTextures.h" />
|
||||
|
@ -188,4 +190,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -197,6 +197,9 @@
|
|||
<ClCompile Include="AbstractFramebuffer.cpp">
|
||||
<Filter>Base</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShaderCache.cpp">
|
||||
<Filter>Shader Managers</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandProcessor.h" />
|
||||
|
@ -378,6 +381,9 @@
|
|||
<ClInclude Include="AbstractFramebuffer.h">
|
||||
<Filter>Base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShaderCache.h">
|
||||
<Filter>Shader Managers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
|
@ -187,6 +187,10 @@ static u32 GetNumAutoShaderCompilerThreads()
|
|||
|
||||
u32 VideoConfig::GetShaderCompilerThreads() const
|
||||
{
|
||||
// videocommon shader cache is currently broken on OGL, needs multiple contexts.
|
||||
if (backend_info.api_type == APIType::OpenGL)
|
||||
return 0;
|
||||
|
||||
if (iShaderCompilerThreads >= 0)
|
||||
return static_cast<u32>(iShaderCompilerThreads);
|
||||
else
|
||||
|
@ -195,6 +199,10 @@ u32 VideoConfig::GetShaderCompilerThreads() const
|
|||
|
||||
u32 VideoConfig::GetShaderPrecompilerThreads() const
|
||||
{
|
||||
// videocommon shader cache is currently broken on OGL, needs multiple contexts.
|
||||
if (backend_info.api_type == APIType::OpenGL)
|
||||
return 0;
|
||||
|
||||
if (iShaderPrecompilerThreads >= 0)
|
||||
return static_cast<u32>(iShaderPrecompilerThreads);
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue