D3D: Uber shader support

This commit is contained in:
Stenzek
2017-07-20 15:25:31 +10:00
parent cd502990fa
commit 4bf5625895
15 changed files with 720 additions and 193 deletions

View File

@ -8,12 +8,15 @@
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/ConfigManager.h"
#include "Core/Host.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/PixelShaderCache.h"
#include "VideoCommon/Debugger.h"
@ -25,10 +28,15 @@
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_ColorMatrixProgram[2] = {nullptr};
ID3D11PixelShader* s_ColorCopyProgram[2] = {nullptr};
@ -429,10 +437,8 @@ ID3D11PixelShader* PixelShaderCache::GetDepthResolveProgram()
return s_DepthResolveProgram;
}
ID3D11Buffer*& PixelShaderCache::GetConstantBuffer()
static void UpdateConstantBuffers()
{
// TODO: divide the global variables of the generated shaders into about 5 constant buffers to
// speed this up
if (PixelShaderManager::dirty)
{
D3D11_MAPPED_SUBRESOURCE map;
@ -443,14 +449,20 @@ ID3D11Buffer*& PixelShaderCache::GetConstantBuffer()
ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(PixelShaderConstants));
}
}
ID3D11Buffer* PixelShaderCache::GetConstantBuffer()
{
UpdateConstantBuffers();
return pscbuf;
}
// this class will load the precompiled shaders into our cache
class PixelShaderCacheInserter : public LinearDiskCacheReader<PixelShaderUid, u8>
template <typename UidType>
class PixelShaderCacheInserter : public LinearDiskCacheReader<UidType, u8>
{
public:
void Read(const PixelShaderUid& key, const u8* value, u32 value_size)
void Read(const UidType& key, const u8* value, u32 value_size)
{
PixelShaderCache::InsertByteCode(key, value, value_size);
}
@ -499,22 +511,34 @@ void PixelShaderCache::Init()
if (g_ActiveConfig.bShaderCache)
LoadShaderCache();
if (g_ActiveConfig.CanPrecompileUberShaders())
PrecompileUberShaders();
}
void PixelShaderCache::LoadShaderCache()
{
PixelShaderCacheInserter inserter;
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())
PrecompileUberShaders();
}
// ONLY to be used during shutdown.
@ -522,10 +546,15 @@ 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
@ -558,82 +587,252 @@ void PixelShaderCache::Shutdown()
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()
{
PixelShaderUid uid = GetPixelShaderUid();
// Check if the shader is already set
if (last_entry)
if (g_ActiveConfig.CanUseUberShaders() &&
(g_ActiveConfig.bDisableSpecializedShaders || g_ActiveConfig.bForcePixelUberShaders))
{
if (uid == last_uid)
{
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
return (last_entry->shader != nullptr);
}
return SetUberShader();
}
last_uid = uid;
PixelShaderUid uid = GetPixelShaderUid();
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
PSCache::iterator iter;
iter = PixelShaders.find(uid);
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);
return (entry.shader != nullptr);
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());
D3DBlob* pbytecode;
if (!D3D::CompilePixelShader(code.GetBuffer(), &pbytecode))
D3D::CompilePixelShader(code.GetBuffer(), &bytecode);
if (!InsertByteCode(uid, bytecode->Data(), bytecode->Size()))
{
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
SAFE_RELEASE(bytecode);
return false;
}
// Insert the bytecode into the caches
g_ps_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size());
bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size());
pbytecode->Release();
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
return success;
g_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size());
return SetShader();
}
bool PixelShaderCache::InsertByteCode(const PixelShaderUid& uid, const void* bytecode,
unsigned int bytecodelen)
bool PixelShaderCache::SetUberShader()
{
ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen);
if (shader == nullptr)
return false;
UberShader::PixelShaderUid uid = UberShader::GetPixelShaderUid();
// TODO: Somehow make the debug name a bit more specific
D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of PixelShaderCache");
// Make an entry in the table
PSCacheEntry newentry;
newentry.shader = shader;
PixelShaders[uid] = newentry;
last_entry = &PixelShaders[uid];
if (!shader)
if (last_uber_entry && last_uber_uid == uid)
{
// INCSTAT(stats.numPixelShadersFailed);
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->Data(), bytecode->Size()))
{
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;
}
INCSTAT(stats.numPixelShadersCreated);
SETSTAT(stats.numPixelShadersAlive, PixelShaders.size());
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::PrecompileUberShaders()
{
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