1
0
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:
Philip Rebohle 2018-04-06 17:54:02 +02:00
parent 38291356a7
commit 00a452ed89
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
5 changed files with 190 additions and 58 deletions

View File

@ -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());

View File

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

View File

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

View File

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

View File

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