mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-11-30 13:24:10 +01:00
[d3d11] Implement DXBC shader module cache
If an application compiles the same shader multiple times, we should reuse an already existing DxvkShaderModule instead of creating a new one. This helps keep the number of DxvkGraphicsPipeline objects low in games such as Rise of the Tomb Raider.
This commit is contained in:
parent
38291356a7
commit
00a452ed89
@ -1007,7 +1007,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::VertexShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppVertexShader == nullptr)
|
if (ppVertexShader == nullptr)
|
||||||
@ -1028,7 +1029,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::GeometryShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppGeometryShader == nullptr)
|
if (ppGeometryShader == nullptr)
|
||||||
@ -1065,7 +1067,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::PixelShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppPixelShader == nullptr)
|
if (ppPixelShader == nullptr)
|
||||||
@ -1086,7 +1089,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::HullShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppHullShader == nullptr)
|
if (ppHullShader == nullptr)
|
||||||
@ -1107,7 +1111,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::DomainShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppDomainShader == nullptr)
|
if (ppDomainShader == nullptr)
|
||||||
@ -1128,7 +1133,8 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule module;
|
D3D11ShaderModule module;
|
||||||
|
|
||||||
if (FAILED(this->CreateShaderModule(&module,
|
if (FAILED(this->CreateShaderModule(&module,
|
||||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||||
|
DxbcProgramType::ComputeShader)))
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
if (ppComputeShader == nullptr)
|
if (ppComputeShader == nullptr)
|
||||||
@ -1754,13 +1760,14 @@ namespace dxvk {
|
|||||||
D3D11ShaderModule* pShaderModule,
|
D3D11ShaderModule* pShaderModule,
|
||||||
const void* pShaderBytecode,
|
const void* pShaderBytecode,
|
||||||
size_t BytecodeLength,
|
size_t BytecodeLength,
|
||||||
ID3D11ClassLinkage* pClassLinkage) {
|
ID3D11ClassLinkage* pClassLinkage,
|
||||||
|
DxbcProgramType ProgramType) {
|
||||||
if (pClassLinkage != nullptr)
|
if (pClassLinkage != nullptr)
|
||||||
Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported");
|
Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
*pShaderModule = D3D11ShaderModule(
|
*pShaderModule = m_shaderModules.GetShaderModule(
|
||||||
&m_dxbcOptions, this, pShaderBytecode, BytecodeLength);
|
&m_dxbcOptions, pShaderBytecode, BytecodeLength, ProgramType);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
} catch (const DxvkError& e) {
|
} catch (const DxvkError& e) {
|
||||||
Logger::err(e.message());
|
Logger::err(e.message());
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "d3d11_interfaces.h"
|
#include "d3d11_interfaces.h"
|
||||||
#include "d3d11_options.h"
|
#include "d3d11_options.h"
|
||||||
|
#include "d3d11_shader.h"
|
||||||
#include "d3d11_state.h"
|
#include "d3d11_state.h"
|
||||||
#include "d3d11_util.h"
|
#include "d3d11_util.h"
|
||||||
|
|
||||||
@ -363,12 +364,14 @@ namespace dxvk {
|
|||||||
D3D11StateObjectSet<D3D11DepthStencilState> m_dsStateObjects;
|
D3D11StateObjectSet<D3D11DepthStencilState> m_dsStateObjects;
|
||||||
D3D11StateObjectSet<D3D11RasterizerState> m_rsStateObjects;
|
D3D11StateObjectSet<D3D11RasterizerState> m_rsStateObjects;
|
||||||
D3D11StateObjectSet<D3D11SamplerState> m_samplerObjects;
|
D3D11StateObjectSet<D3D11SamplerState> m_samplerObjects;
|
||||||
|
D3D11ShaderModuleSet m_shaderModules;
|
||||||
|
|
||||||
HRESULT CreateShaderModule(
|
HRESULT CreateShaderModule(
|
||||||
D3D11ShaderModule* pShaderModule,
|
D3D11ShaderModule* pShaderModule,
|
||||||
const void* pShaderBytecode,
|
const void* pShaderBytecode,
|
||||||
size_t BytecodeLength,
|
size_t BytecodeLength,
|
||||||
ID3D11ClassLinkage* pClassLinkage);
|
ID3D11ClassLinkage* pClassLinkage,
|
||||||
|
DxbcProgramType ProgramType);
|
||||||
|
|
||||||
void InitBuffer(
|
void InitBuffer(
|
||||||
D3D11Buffer* pBuffer,
|
D3D11Buffer* pBuffer,
|
||||||
|
@ -3,29 +3,61 @@
|
|||||||
|
|
||||||
namespace dxvk {
|
namespace dxvk {
|
||||||
|
|
||||||
|
D3D11ShaderKey::D3D11ShaderKey(
|
||||||
|
DxbcProgramType ProgramType,
|
||||||
|
const void* pShaderBytecode,
|
||||||
|
size_t BytecodeLength)
|
||||||
|
: m_type(ProgramType),
|
||||||
|
m_hash(Sha1Hash::compute(
|
||||||
|
reinterpret_cast<const uint8_t*>(pShaderBytecode),
|
||||||
|
BytecodeLength)) { }
|
||||||
|
|
||||||
|
|
||||||
|
std::string D3D11ShaderKey::GetName() const {
|
||||||
|
static const std::array<const char*, 6> s_prefix
|
||||||
|
= {{ "PS_", "VS_", "GS_", "HS_", "DS_", "CS_" }};
|
||||||
|
|
||||||
|
return str::format(
|
||||||
|
s_prefix.at(uint32_t(m_type)),
|
||||||
|
m_hash.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t D3D11ShaderKey::GetHash() const {
|
||||||
|
DxvkHashState result;
|
||||||
|
result.add(uint32_t(m_type));
|
||||||
|
|
||||||
|
const uint8_t* digest = m_hash.digest();
|
||||||
|
for (uint32_t i = 0; i < 5; i++) {
|
||||||
|
result.add(
|
||||||
|
uint32_t(digest[4 + i + 0]) << 0
|
||||||
|
| uint32_t(digest[4 + i + 1]) << 8
|
||||||
|
| uint32_t(digest[4 + i + 2]) << 16
|
||||||
|
| uint32_t(digest[4 + i + 3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
D3D11ShaderModule:: D3D11ShaderModule() { }
|
D3D11ShaderModule:: D3D11ShaderModule() { }
|
||||||
D3D11ShaderModule::~D3D11ShaderModule() { }
|
D3D11ShaderModule::~D3D11ShaderModule() { }
|
||||||
|
|
||||||
|
|
||||||
D3D11ShaderModule::D3D11ShaderModule(
|
D3D11ShaderModule::D3D11ShaderModule(
|
||||||
const DxbcOptions* pDxbcOptions,
|
const D3D11ShaderKey* pShaderKey,
|
||||||
D3D11Device* pDevice,
|
const DxbcOptions* pDxbcOptions,
|
||||||
const void* pShaderBytecode,
|
const void* pShaderBytecode,
|
||||||
size_t BytecodeLength) {
|
size_t BytecodeLength)
|
||||||
|
: m_name(pShaderKey->GetName()) {
|
||||||
|
Logger::debug(str::format("Compiling shader ", m_name));
|
||||||
|
|
||||||
DxbcReader reader(
|
DxbcReader reader(
|
||||||
reinterpret_cast<const char*>(pShaderBytecode),
|
reinterpret_cast<const char*>(pShaderBytecode),
|
||||||
BytecodeLength);
|
BytecodeLength);
|
||||||
|
|
||||||
DxbcModule module(reader);
|
DxbcModule module(reader);
|
||||||
|
|
||||||
// Construct the shader name that we'll use for
|
|
||||||
// debug messages and as the dump/read file name
|
|
||||||
m_name = ConstructFileName(
|
|
||||||
ComputeShaderHash(pShaderBytecode, BytecodeLength),
|
|
||||||
module.version().type());
|
|
||||||
|
|
||||||
Logger::debug(str::format("Compiling shader ", m_name));
|
|
||||||
|
|
||||||
// If requested by the user, dump both the raw DXBC
|
// If requested by the user, dump both the raw DXBC
|
||||||
// shader and the compiled SPIR-V module to a file.
|
// shader and the compiled SPIR-V module to a file.
|
||||||
const std::string dumpPath = env::getEnvVar(L"DXVK_SHADER_DUMP_PATH");
|
const std::string dumpPath = env::getEnvVar(L"DXVK_SHADER_DUMP_PATH");
|
||||||
@ -61,30 +93,40 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sha1Hash D3D11ShaderModule::ComputeShaderHash(
|
D3D11ShaderModuleSet:: D3D11ShaderModuleSet() { }
|
||||||
const void* pShaderBytecode,
|
D3D11ShaderModuleSet::~D3D11ShaderModuleSet() { }
|
||||||
size_t BytecodeLength) const {
|
|
||||||
return Sha1Hash::compute(
|
|
||||||
reinterpret_cast<const uint8_t*>(pShaderBytecode),
|
|
||||||
BytecodeLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string D3D11ShaderModule::ConstructFileName(
|
D3D11ShaderModule D3D11ShaderModuleSet::GetShaderModule(
|
||||||
const Sha1Hash& hash,
|
const DxbcOptions* pDxbcOptions,
|
||||||
const DxbcProgramType& type) const {
|
const void* pShaderBytecode,
|
||||||
std::string typeStr;
|
size_t BytecodeLength,
|
||||||
|
DxbcProgramType ProgramType) {
|
||||||
|
// Compute the shader's unique key so that we can perform a lookup
|
||||||
|
D3D11ShaderKey key(ProgramType, pShaderBytecode, BytecodeLength);
|
||||||
|
|
||||||
switch (type) {
|
{ std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
case DxbcProgramType::PixelShader: typeStr = "PS_"; break;
|
|
||||||
case DxbcProgramType::VertexShader: typeStr = "VS_"; break;
|
auto entry = m_modules.find(key);
|
||||||
case DxbcProgramType::GeometryShader: typeStr = "GS_"; break;
|
if (entry != m_modules.end())
|
||||||
case DxbcProgramType::HullShader: typeStr = "HS_"; break;
|
return entry->second;
|
||||||
case DxbcProgramType::DomainShader: typeStr = "DS_"; break;
|
|
||||||
case DxbcProgramType::ComputeShader: typeStr = "CS_"; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str::format(typeStr, hash.toString());
|
// This shader has not been compiled yet, so we have to create a
|
||||||
|
// new module. This takes a while, so we won't lock the structure.
|
||||||
|
D3D11ShaderModule module(&key, pDxbcOptions, pShaderBytecode, BytecodeLength);
|
||||||
|
|
||||||
|
// Insert the new module into the lookup table. If another thread
|
||||||
|
// has compiled the same shader in the meantime, we should return
|
||||||
|
// that object instead and discard the newly created module.
|
||||||
|
{ std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
auto status = m_modules.insert({ key, module });
|
||||||
|
if (!status.second)
|
||||||
|
return status.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <dxbc_module.h>
|
#include <mutex>
|
||||||
#include <dxvk_device.h>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "../dxbc/dxbc_module.h"
|
||||||
|
#include "../dxvk/dxvk_device.h"
|
||||||
|
|
||||||
#include "../util/sha1/sha1_util.h"
|
#include "../util/sha1/sha1_util.h"
|
||||||
|
|
||||||
@ -14,10 +17,51 @@ namespace dxvk {
|
|||||||
|
|
||||||
class D3D11Device;
|
class D3D11Device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader key
|
||||||
|
*
|
||||||
|
* A unique identifier for a shader consisting
|
||||||
|
* of the program type and the SHA-1 hash of
|
||||||
|
* the shader's original bytecode.
|
||||||
|
*/
|
||||||
|
class D3D11ShaderKey {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D11ShaderKey(
|
||||||
|
DxbcProgramType ProgramType,
|
||||||
|
const void* pShaderBytecode,
|
||||||
|
size_t BytecodeLength);
|
||||||
|
|
||||||
|
std::string GetName() const;
|
||||||
|
|
||||||
|
size_t GetHash() const;
|
||||||
|
|
||||||
|
bool operator == (const D3D11ShaderKey& other) const {
|
||||||
|
return m_type == other.m_type
|
||||||
|
&& m_hash == other.m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DxbcProgramType m_type;
|
||||||
|
Sha1Hash m_hash;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D3D11ShaderKeyHash {
|
||||||
|
size_t operator () (const D3D11ShaderKey& a) const {
|
||||||
|
return a.GetHash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Shader module
|
* \brief Shader module
|
||||||
*
|
*
|
||||||
*
|
* Stores the compiled SPIR-V shader and the SHA-1
|
||||||
|
* hash of the original DXBC shader, which can be
|
||||||
|
* used to identify the shader.
|
||||||
*/
|
*/
|
||||||
class D3D11ShaderModule {
|
class D3D11ShaderModule {
|
||||||
|
|
||||||
@ -25,17 +69,17 @@ namespace dxvk {
|
|||||||
|
|
||||||
D3D11ShaderModule();
|
D3D11ShaderModule();
|
||||||
D3D11ShaderModule(
|
D3D11ShaderModule(
|
||||||
const DxbcOptions* pDxbcOptions,
|
const D3D11ShaderKey* pShaderKey,
|
||||||
D3D11Device* pDevice,
|
const DxbcOptions* pDxbcOptions,
|
||||||
const void* pShaderBytecode,
|
const void* pShaderBytecode,
|
||||||
size_t BytecodeLength);
|
size_t BytecodeLength);
|
||||||
~D3D11ShaderModule();
|
~D3D11ShaderModule();
|
||||||
|
|
||||||
Rc<DxvkShader> GetShader() const {
|
Rc<DxvkShader> GetShader() const {
|
||||||
return m_shader;
|
return m_shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& GetName() const {
|
std::string GetName() const {
|
||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +88,6 @@ namespace dxvk {
|
|||||||
std::string m_name;
|
std::string m_name;
|
||||||
Rc<DxvkShader> m_shader;
|
Rc<DxvkShader> m_shader;
|
||||||
|
|
||||||
Sha1Hash ComputeShaderHash(
|
|
||||||
const void* pShaderBytecode,
|
|
||||||
size_t BytecodeLength) const;
|
|
||||||
|
|
||||||
std::string ConstructFileName(
|
|
||||||
const Sha1Hash& hash,
|
|
||||||
const DxbcProgramType& type) const;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +103,7 @@ namespace dxvk {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
D3D11Shader(D3D11Device* device, D3D11ShaderModule&& module)
|
D3D11Shader(D3D11Device* device, const D3D11ShaderModule& module)
|
||||||
: m_device(device), m_module(std::move(module)) { }
|
: m_device(device), m_module(std::move(module)) { }
|
||||||
|
|
||||||
~D3D11Shader() { }
|
~D3D11Shader() { }
|
||||||
@ -112,4 +148,37 @@ namespace dxvk {
|
|||||||
using D3D11PixelShader = D3D11Shader<ID3D11PixelShader>;
|
using D3D11PixelShader = D3D11Shader<ID3D11PixelShader>;
|
||||||
using D3D11ComputeShader = D3D11Shader<ID3D11ComputeShader>;
|
using D3D11ComputeShader = D3D11Shader<ID3D11ComputeShader>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shader module set
|
||||||
|
*
|
||||||
|
* Some applications may compile the same shader multiple
|
||||||
|
* times, so we should cache the resulting shader modules
|
||||||
|
* and reuse them rather than creating new ones. This
|
||||||
|
* class is thread-safe.
|
||||||
|
*/
|
||||||
|
class D3D11ShaderModuleSet {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
D3D11ShaderModuleSet();
|
||||||
|
~D3D11ShaderModuleSet();
|
||||||
|
|
||||||
|
D3D11ShaderModule GetShaderModule(
|
||||||
|
const DxbcOptions* pDxbcOptions,
|
||||||
|
const void* pShaderBytecode,
|
||||||
|
size_t BytecodeLength,
|
||||||
|
DxbcProgramType ProgramType);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
|
||||||
|
std::unordered_map<
|
||||||
|
D3D11ShaderKey,
|
||||||
|
D3D11ShaderModule,
|
||||||
|
D3D11ShaderKeyHash> m_modules;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,17 @@ namespace dxvk {
|
|||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
|
||||||
|
const uint8_t* digest() const {
|
||||||
|
return m_digest.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const Sha1Hash& other) const {
|
||||||
|
return !std::memcmp(
|
||||||
|
this->m_digest.data(),
|
||||||
|
other.m_digest.data(),
|
||||||
|
other.m_digest.size());
|
||||||
|
}
|
||||||
|
|
||||||
static Sha1Hash compute(
|
static Sha1Hash compute(
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
Loading…
Reference in New Issue
Block a user