diff options
| author | hdcmeta <hdcmeta@gmail.com> | 2016-01-07 19:40:35 -0800 |
|---|---|---|
| committer | hdcmeta <hdcmeta@gmail.com> | 2016-02-15 09:48:25 -0800 |
| commit | 8cc686b3606deb1a3638281da9c12180a481e899 (patch) | |
| tree | 6adae584fcc27911bf4464deb8a62d468472c98c /Source/Core/VideoBackends/D3D12/ShaderCache.cpp | |
| parent | a2e2e36745717135d42df2d17965aa709480e3e3 (diff) | |
D3D12: Initial commit for D3D12 backend implementation.
Diffstat (limited to 'Source/Core/VideoBackends/D3D12/ShaderCache.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/D3D12/ShaderCache.cpp | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/D3D12/ShaderCache.cpp b/Source/Core/VideoBackends/D3D12/ShaderCache.cpp new file mode 100644 index 0000000000..299834434b --- /dev/null +++ b/Source/Core/VideoBackends/D3D12/ShaderCache.cpp @@ -0,0 +1,388 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/LinearDiskCache.h" + +#include "Core/ConfigManager.h" + +#include "VideoBackends/D3D12/D3DCommandListManager.h" +#include "VideoBackends/D3D12/D3DShader.h" +#include "VideoBackends/D3D12/ShaderCache.h" + +#include "VideoCommon/Debugger.h" +#include "VideoCommon/Statistics.h" + +namespace DX12 +{ + +// Primitive topology type is always triangle, unless the GS stage is used. This is consumed +// by the PSO created in Renderer::ApplyState. +static D3D12_PRIMITIVE_TOPOLOGY_TYPE s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + +using GsBytecodeCache = std::map<GeometryShaderUid, D3D12_SHADER_BYTECODE>; +using PsBytecodeCache = std::map<PixelShaderUid, D3D12_SHADER_BYTECODE>; +using VsBytecodeCache = std::map<VertexShaderUid, D3D12_SHADER_BYTECODE>; +GsBytecodeCache s_gs_bytecode_cache; +PsBytecodeCache s_ps_bytecode_cache; +VsBytecodeCache s_vs_bytecode_cache; + +// Used to keep track of blobs to release at Shutdown time. +static std::vector<ID3DBlob*> s_shader_blob_list; + +// Only used for shader debugging.. +using GsHlslCache = std::map<GeometryShaderUid, std::string>; +using PsHlslCache = std::map<PixelShaderUid, std::string>; +using VsHlslCache = std::map<VertexShaderUid, std::string>; +static GsHlslCache s_gs_hlsl_cache; +static PsHlslCache s_ps_hlsl_cache; +static VsHlslCache s_vs_hlsl_cache; + +static LinearDiskCache<GeometryShaderUid, u8> s_gs_disk_cache; +static LinearDiskCache<PixelShaderUid, u8> s_ps_disk_cache; +static LinearDiskCache<VertexShaderUid, u8> s_vs_disk_cache; + +static UidChecker<GeometryShaderUid, ShaderCode> s_geometry_uid_checker; +static UidChecker<PixelShaderUid, ShaderCode> s_pixel_uid_checker; +static UidChecker<VertexShaderUid, ShaderCode> s_vertex_uid_checker; + +static D3D12_SHADER_BYTECODE s_last_geometry_shader_bytecode; +static D3D12_SHADER_BYTECODE s_last_pixel_shader_bytecode; +static D3D12_SHADER_BYTECODE s_last_vertex_shader_bytecode; +static GeometryShaderUid s_last_geometry_shader_uid; +static PixelShaderUid s_last_pixel_shader_uid; +static VertexShaderUid s_last_vertex_shader_uid; + +template<class UidType, class ShaderCacheType, ShaderCacheType* cache> +class ShaderCacheInserter final : public LinearDiskCacheReader<UidType, u8> +{ +public: + void Read(const UidType &key, const u8* value, u32 value_size) + { + ID3DBlob* blob = nullptr; + CheckHR(d3d_create_blob(value_size, &blob)); + memcpy(blob->GetBufferPointer(), value, value_size); + + ShaderCache::InsertByteCode<UidType, ShaderCacheType>(key, cache, blob); + } +}; + +void ShaderCache::Init() +{ + // This class intentionally shares its shader cache files with DX11, as the shaders are (right now) identical. + // Reduces unnecessary compilation when switching between APIs. + + s_last_geometry_shader_bytecode = {}; + s_last_pixel_shader_bytecode = {}; + s_last_vertex_shader_bytecode = {}; + s_last_geometry_shader_uid = {}; + s_last_pixel_shader_uid = {}; + s_last_vertex_shader_uid = {}; + + // Ensure shader cache directory exists.. + std::string shader_cache_path = File::GetUserPath(D_SHADERCACHE_IDX); + + if (!File::Exists(shader_cache_path)) + File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); + + std::string title_unique_id = SConfig::GetInstance().m_strUniqueID.c_str(); + + std::string gs_cache_filename = StringFromFormat("%sdx11-%s-gs.cache", shader_cache_path.c_str(), title_unique_id.c_str()); + std::string ps_cache_filename = StringFromFormat("%sdx11-%s-ps.cache", shader_cache_path.c_str(), title_unique_id.c_str()); + std::string vs_cache_filename = StringFromFormat("%sdx11-%s-vs.cache", shader_cache_path.c_str(), title_unique_id.c_str()); + + ShaderCacheInserter<GeometryShaderUid, GsBytecodeCache, &s_gs_bytecode_cache> gs_inserter; + s_gs_disk_cache.OpenAndRead(gs_cache_filename, gs_inserter); + + ShaderCacheInserter<PixelShaderUid, PsBytecodeCache, &s_ps_bytecode_cache> ps_inserter; + s_ps_disk_cache.OpenAndRead(ps_cache_filename, ps_inserter); + + ShaderCacheInserter<VertexShaderUid, VsBytecodeCache, &s_vs_bytecode_cache> vs_inserter; + s_vs_disk_cache.OpenAndRead(vs_cache_filename, vs_inserter); + + // Clear out cache when debugging shaders to ensure stale ones don't stick around.. + if (g_Config.bEnableShaderDebugging) + Clear(); + + SETSTAT(stats.numPixelShadersAlive, static_cast<int>(s_ps_bytecode_cache.size())); + SETSTAT(stats.numPixelShadersCreated, static_cast<int>(s_ps_bytecode_cache.size())); + SETSTAT(stats.numVertexShadersAlive, static_cast<int>(s_vs_bytecode_cache.size())); + SETSTAT(stats.numVertexShadersCreated, static_cast<int>(s_vs_bytecode_cache.size())); +} + +void ShaderCache::Clear() +{ + for (auto& iter : s_shader_blob_list) + SAFE_RELEASE(iter); + + s_shader_blob_list.clear(); + + s_gs_bytecode_cache.clear(); + s_ps_bytecode_cache.clear(); + s_vs_bytecode_cache.clear(); + + s_last_geometry_shader_bytecode = {}; + s_last_geometry_shader_uid = {}; + + s_last_pixel_shader_bytecode = {}; + s_last_pixel_shader_uid = {}; + + s_last_vertex_shader_bytecode = {}; + s_last_vertex_shader_uid = {}; +} + +void ShaderCache::Shutdown() +{ + Clear(); + + s_gs_disk_cache.Sync(); + s_gs_disk_cache.Close(); + s_ps_disk_cache.Sync(); + s_ps_disk_cache.Close(); + s_vs_disk_cache.Sync(); + s_vs_disk_cache.Close(); + + if (g_Config.bEnableShaderDebugging) + { + s_gs_hlsl_cache.clear(); + s_ps_hlsl_cache.clear(); + s_vs_hlsl_cache.clear(); + } + + s_geometry_uid_checker.Invalidate(); + s_pixel_uid_checker.Invalidate(); + s_vertex_uid_checker.Invalidate(); +} + +void ShaderCache::LoadAndSetActiveShaders(DSTALPHA_MODE ps_dst_alpha_mode, u32 gs_primitive_type) +{ + SetCurrentPrimitiveTopology(gs_primitive_type); + + GeometryShaderUid gs_uid = GetGeometryShaderUid(gs_primitive_type, API_D3D); + PixelShaderUid ps_uid = GetPixelShaderUid(ps_dst_alpha_mode, API_D3D); + VertexShaderUid vs_uid = GetVertexShaderUid(API_D3D); + + bool gs_changed = gs_uid != s_last_geometry_shader_uid; + bool ps_changed = ps_uid != s_last_pixel_shader_uid; + bool vs_changed = vs_uid != s_last_vertex_shader_uid; + + if (!gs_changed && !ps_changed && !vs_changed) + { + return; + } + + if (gs_changed) + { + HandleGSUIDChange(gs_uid, gs_primitive_type); + } + + if (ps_changed) + { + HandlePSUIDChange(ps_uid, ps_dst_alpha_mode); + } + + if (vs_changed) + { + HandleVSUIDChange(vs_uid); + } + + // A Uid has changed, so the PSO will need to be reset at next ApplyState. + D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PSO, true); +} + +void ShaderCache::SetCurrentPrimitiveTopology(u32 gs_primitive_type) +{ + switch (gs_primitive_type) + { + case PRIMITIVE_TRIANGLES: + s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + break; + case PRIMITIVE_LINES: + s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; + break; + case PRIMITIVE_POINTS: + s_current_primitive_topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; + break; + default: + CHECK(0, "Invalid primitive type."); + break; + } +} + +void ShaderCache::HandleGSUIDChange(GeometryShaderUid gs_uid, u32 gs_primitive_type) +{ + s_last_geometry_shader_uid = gs_uid; + + if (g_ActiveConfig.bEnableShaderDebugging) + { + ShaderCode code = GenerateGeometryShaderCode(gs_primitive_type, API_D3D); + s_geometry_uid_checker.AddToIndexAndCheck(code, gs_uid, "Geometry", "g"); + } + + if (gs_uid.GetUidData()->IsPassthrough()) + { + s_last_geometry_shader_bytecode = {}; + return; + } + + auto gs_iterator = s_gs_bytecode_cache.find(gs_uid); + if (gs_iterator != s_gs_bytecode_cache.end()) + { + s_last_geometry_shader_bytecode = gs_iterator->second; + } + else + { + ShaderCode gs_code = GenerateGeometryShaderCode(gs_primitive_type, API_D3D); + ID3DBlob* gs_bytecode = nullptr; + + if (!D3D::CompileGeometryShader(gs_code.GetBuffer(), &gs_bytecode)) + { + GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); + return; + } + + s_last_geometry_shader_bytecode = InsertByteCode<GeometryShaderUid, GsBytecodeCache>(gs_uid, &s_gs_bytecode_cache, gs_bytecode); + s_gs_disk_cache.Append(gs_uid, reinterpret_cast<u8*>(gs_bytecode->GetBufferPointer()), static_cast<u32>(gs_bytecode->GetBufferSize())); + + if (g_ActiveConfig.bEnableShaderDebugging && gs_bytecode) + { + s_gs_hlsl_cache[gs_uid] = gs_code.GetBuffer(); + } + } +} + +void ShaderCache::HandlePSUIDChange(PixelShaderUid ps_uid, DSTALPHA_MODE ps_dst_alpha_mode) +{ + s_last_pixel_shader_uid = ps_uid; + + if (g_ActiveConfig.bEnableShaderDebugging) + { + ShaderCode code = GeneratePixelShaderCode(ps_dst_alpha_mode, API_D3D); + s_pixel_uid_checker.AddToIndexAndCheck(code, ps_uid, "Pixel", "p"); + } + + auto ps_iterator = s_ps_bytecode_cache.find(ps_uid); + if (ps_iterator != s_ps_bytecode_cache.end()) + { + s_last_pixel_shader_bytecode = ps_iterator->second; + GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); + } + else + { + ShaderCode ps_code = GeneratePixelShaderCode(ps_dst_alpha_mode, API_D3D); + ID3DBlob* ps_bytecode = nullptr; + + if (!D3D::CompilePixelShader(ps_code.GetBuffer(), &ps_bytecode)) + { + GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); + return; + } + + s_last_pixel_shader_bytecode = InsertByteCode<PixelShaderUid, PsBytecodeCache>(ps_uid, &s_ps_bytecode_cache, ps_bytecode); + s_ps_disk_cache.Append(ps_uid, reinterpret_cast<u8*>(ps_bytecode->GetBufferPointer()), static_cast<u32>(ps_bytecode->GetBufferSize())); + + SETSTAT(stats.numPixelShadersAlive, static_cast<int>(s_ps_bytecode_cache.size())); + INCSTAT(stats.numPixelShadersCreated); + + if (g_ActiveConfig.bEnableShaderDebugging && ps_bytecode) + { + s_ps_hlsl_cache[ps_uid] = ps_code.GetBuffer(); + } + } +} + +void ShaderCache::HandleVSUIDChange(VertexShaderUid vs_uid) +{ + s_last_vertex_shader_uid = vs_uid; + + if (g_ActiveConfig.bEnableShaderDebugging) + { + ShaderCode code = GenerateVertexShaderCode(API_D3D); + s_vertex_uid_checker.AddToIndexAndCheck(code, vs_uid, "Vertex", "v"); + } + + auto vs_iterator = s_vs_bytecode_cache.find(vs_uid); + if (vs_iterator != s_vs_bytecode_cache.end()) + { + s_last_vertex_shader_bytecode = vs_iterator->second; + GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); + } + else + { + ShaderCode vs_code = GenerateVertexShaderCode(API_D3D); + ID3DBlob* vs_bytecode = nullptr; + + if (!D3D::CompileVertexShader(vs_code.GetBuffer(), &vs_bytecode)) + { + GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); + return; + } + + s_last_vertex_shader_bytecode = InsertByteCode<VertexShaderUid, VsBytecodeCache>(vs_uid, &s_vs_bytecode_cache, vs_bytecode); + s_vs_disk_cache.Append(vs_uid, reinterpret_cast<u8*>(vs_bytecode->GetBufferPointer()), static_cast<u32>(vs_bytecode->GetBufferSize())); + + SETSTAT(stats.numVertexShadersAlive, static_cast<int>(s_vs_bytecode_cache.size())); + INCSTAT(stats.numVertexShadersCreated); + + if (g_ActiveConfig.bEnableShaderDebugging && vs_bytecode) + { + s_vs_hlsl_cache[vs_uid] = vs_code.GetBuffer(); + } + } +} + +template<class UidType, class ShaderCacheType> +D3D12_SHADER_BYTECODE ShaderCache::InsertByteCode(const UidType& uid, ShaderCacheType* shader_cache, ID3DBlob* bytecode_blob) +{ + // Note: Don't release the incoming bytecode, we need it to stick around, since in D3D12 + // the raw bytecode itself is bound. It is released at Shutdown() time. + + s_shader_blob_list.push_back(bytecode_blob); + + D3D12_SHADER_BYTECODE shader_bytecode; + shader_bytecode.pShaderBytecode = bytecode_blob->GetBufferPointer(); + shader_bytecode.BytecodeLength = bytecode_blob->GetBufferSize(); + + (*shader_cache)[uid] = shader_bytecode; + + return shader_bytecode; +} + +D3D12_PRIMITIVE_TOPOLOGY_TYPE ShaderCache::GetCurrentPrimitiveTopology() { return s_current_primitive_topology; } + +D3D12_SHADER_BYTECODE ShaderCache::GetActiveGeometryShaderBytecode() { return s_last_geometry_shader_bytecode; } +D3D12_SHADER_BYTECODE ShaderCache::GetActivePixelShaderBytecode() { return s_last_pixel_shader_bytecode; } +D3D12_SHADER_BYTECODE ShaderCache::GetActiveVertexShaderBytecode() { return s_last_vertex_shader_bytecode; } + +const GeometryShaderUid* ShaderCache::GetActiveGeometryShaderUid() { return &s_last_geometry_shader_uid; } +const PixelShaderUid* ShaderCache::GetActivePixelShaderUid() { return &s_last_pixel_shader_uid; } +const VertexShaderUid* ShaderCache::GetActiveVertexShaderUid() { return &s_last_vertex_shader_uid; } + +D3D12_SHADER_BYTECODE ShaderCache::GetGeometryShaderFromUid(const GeometryShaderUid* uid) +{ + auto bytecode = s_gs_bytecode_cache.find(*uid); + if (bytecode != s_gs_bytecode_cache.end()) + return bytecode->second; + + return D3D12_SHADER_BYTECODE(); +} + +D3D12_SHADER_BYTECODE ShaderCache::GetPixelShaderFromUid(const PixelShaderUid* uid) +{ + auto bytecode = s_ps_bytecode_cache.find(*uid); + if (bytecode != s_ps_bytecode_cache.end()) + return bytecode->second; + + return D3D12_SHADER_BYTECODE(); +} + +D3D12_SHADER_BYTECODE ShaderCache::GetVertexShaderFromUid(const VertexShaderUid* uid) +{ + auto bytecode = s_vs_bytecode_cache.find(*uid); + if (bytecode != s_vs_bytecode_cache.end()) + return bytecode->second; + + return D3D12_SHADER_BYTECODE(); +} + +}
\ No newline at end of file |
