mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-11-30 04:24:11 +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;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::VertexShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppVertexShader == nullptr)
|
||||
@ -1028,7 +1029,8 @@ namespace dxvk {
|
||||
D3D11ShaderModule module;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::GeometryShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppGeometryShader == nullptr)
|
||||
@ -1065,7 +1067,8 @@ namespace dxvk {
|
||||
D3D11ShaderModule module;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::PixelShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppPixelShader == nullptr)
|
||||
@ -1086,7 +1089,8 @@ namespace dxvk {
|
||||
D3D11ShaderModule module;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::HullShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppHullShader == nullptr)
|
||||
@ -1107,7 +1111,8 @@ namespace dxvk {
|
||||
D3D11ShaderModule module;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::DomainShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppDomainShader == nullptr)
|
||||
@ -1128,7 +1133,8 @@ namespace dxvk {
|
||||
D3D11ShaderModule module;
|
||||
|
||||
if (FAILED(this->CreateShaderModule(&module,
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage)))
|
||||
pShaderBytecode, BytecodeLength, pClassLinkage,
|
||||
DxbcProgramType::ComputeShader)))
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (ppComputeShader == nullptr)
|
||||
@ -1754,13 +1760,14 @@ namespace dxvk {
|
||||
D3D11ShaderModule* pShaderModule,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength,
|
||||
ID3D11ClassLinkage* pClassLinkage) {
|
||||
ID3D11ClassLinkage* pClassLinkage,
|
||||
DxbcProgramType ProgramType) {
|
||||
if (pClassLinkage != nullptr)
|
||||
Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported");
|
||||
|
||||
try {
|
||||
*pShaderModule = D3D11ShaderModule(
|
||||
&m_dxbcOptions, this, pShaderBytecode, BytecodeLength);
|
||||
*pShaderModule = m_shaderModules.GetShaderModule(
|
||||
&m_dxbcOptions, pShaderBytecode, BytecodeLength, ProgramType);
|
||||
return S_OK;
|
||||
} catch (const DxvkError& e) {
|
||||
Logger::err(e.message());
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "d3d11_interfaces.h"
|
||||
#include "d3d11_options.h"
|
||||
#include "d3d11_shader.h"
|
||||
#include "d3d11_state.h"
|
||||
#include "d3d11_util.h"
|
||||
|
||||
@ -363,12 +364,14 @@ namespace dxvk {
|
||||
D3D11StateObjectSet<D3D11DepthStencilState> m_dsStateObjects;
|
||||
D3D11StateObjectSet<D3D11RasterizerState> m_rsStateObjects;
|
||||
D3D11StateObjectSet<D3D11SamplerState> m_samplerObjects;
|
||||
D3D11ShaderModuleSet m_shaderModules;
|
||||
|
||||
HRESULT CreateShaderModule(
|
||||
D3D11ShaderModule* pShaderModule,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength,
|
||||
ID3D11ClassLinkage* pClassLinkage);
|
||||
ID3D11ClassLinkage* pClassLinkage,
|
||||
DxbcProgramType ProgramType);
|
||||
|
||||
void InitBuffer(
|
||||
D3D11Buffer* pBuffer,
|
||||
|
@ -3,29 +3,61 @@
|
||||
|
||||
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(
|
||||
const DxbcOptions* pDxbcOptions,
|
||||
D3D11Device* pDevice,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength) {
|
||||
const D3D11ShaderKey* pShaderKey,
|
||||
const DxbcOptions* pDxbcOptions,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength)
|
||||
: m_name(pShaderKey->GetName()) {
|
||||
Logger::debug(str::format("Compiling shader ", m_name));
|
||||
|
||||
DxbcReader reader(
|
||||
reinterpret_cast<const char*>(pShaderBytecode),
|
||||
BytecodeLength);
|
||||
|
||||
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
|
||||
// shader and the compiled SPIR-V module to a file.
|
||||
const std::string dumpPath = env::getEnvVar(L"DXVK_SHADER_DUMP_PATH");
|
||||
@ -61,30 +93,40 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
Sha1Hash D3D11ShaderModule::ComputeShaderHash(
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength) const {
|
||||
return Sha1Hash::compute(
|
||||
reinterpret_cast<const uint8_t*>(pShaderBytecode),
|
||||
BytecodeLength);
|
||||
}
|
||||
D3D11ShaderModuleSet:: D3D11ShaderModuleSet() { }
|
||||
D3D11ShaderModuleSet::~D3D11ShaderModuleSet() { }
|
||||
|
||||
|
||||
std::string D3D11ShaderModule::ConstructFileName(
|
||||
const Sha1Hash& hash,
|
||||
const DxbcProgramType& type) const {
|
||||
std::string typeStr;
|
||||
D3D11ShaderModule D3D11ShaderModuleSet::GetShaderModule(
|
||||
const DxbcOptions* pDxbcOptions,
|
||||
const void* pShaderBytecode,
|
||||
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) {
|
||||
case DxbcProgramType::PixelShader: typeStr = "PS_"; break;
|
||||
case DxbcProgramType::VertexShader: typeStr = "VS_"; break;
|
||||
case DxbcProgramType::GeometryShader: typeStr = "GS_"; break;
|
||||
case DxbcProgramType::HullShader: typeStr = "HS_"; break;
|
||||
case DxbcProgramType::DomainShader: typeStr = "DS_"; break;
|
||||
case DxbcProgramType::ComputeShader: typeStr = "CS_"; break;
|
||||
{ std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
auto entry = m_modules.find(key);
|
||||
if (entry != m_modules.end())
|
||||
return entry->second;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include <dxbc_module.h>
|
||||
#include <dxvk_device.h>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../dxbc/dxbc_module.h"
|
||||
#include "../dxvk/dxvk_device.h"
|
||||
|
||||
#include "../util/sha1/sha1_util.h"
|
||||
|
||||
@ -14,10 +17,51 @@ namespace dxvk {
|
||||
|
||||
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
|
||||
*
|
||||
*
|
||||
* 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 {
|
||||
|
||||
@ -25,17 +69,17 @@ namespace dxvk {
|
||||
|
||||
D3D11ShaderModule();
|
||||
D3D11ShaderModule(
|
||||
const DxbcOptions* pDxbcOptions,
|
||||
D3D11Device* pDevice,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength);
|
||||
const D3D11ShaderKey* pShaderKey,
|
||||
const DxbcOptions* pDxbcOptions,
|
||||
const void* pShaderBytecode,
|
||||
size_t BytecodeLength);
|
||||
~D3D11ShaderModule();
|
||||
|
||||
Rc<DxvkShader> GetShader() const {
|
||||
return m_shader;
|
||||
}
|
||||
|
||||
const std::string& GetName() const {
|
||||
std::string GetName() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
@ -44,14 +88,6 @@ namespace dxvk {
|
||||
std::string m_name;
|
||||
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:
|
||||
|
||||
D3D11Shader(D3D11Device* device, D3D11ShaderModule&& module)
|
||||
D3D11Shader(D3D11Device* device, const D3D11ShaderModule& module)
|
||||
: m_device(device), m_module(std::move(module)) { }
|
||||
|
||||
~D3D11Shader() { }
|
||||
@ -112,4 +148,37 @@ namespace dxvk {
|
||||
using D3D11PixelShader = D3D11Shader<ID3D11PixelShader>;
|
||||
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;
|
||||
|
||||
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(
|
||||
const uint8_t* data,
|
||||
size_t size);
|
||||
|
Loading…
Reference in New Issue
Block a user