From 764d05bbee290302f47541402ba7878c17eb14e0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 11 Dec 2015 15:03:11 +0100 Subject: [PATCH] Extract out shader caching from D3D to common code --- renderdoc/common/shader_cache.h | 174 +++++++++++++++++++++ renderdoc/driver/d3d11/d3d11_debug.cpp | 204 +++++++------------------ renderdoc/driver/d3d11/d3d11_debug.h | 4 +- renderdoc/renderdoc.vcxproj | 1 + renderdoc/renderdoc.vcxproj.filters | 3 + 5 files changed, 240 insertions(+), 146 deletions(-) create mode 100644 renderdoc/common/shader_cache.h diff --git a/renderdoc/common/shader_cache.h b/renderdoc/common/shader_cache.h new file mode 100644 index 000000000..5b9b0d65a --- /dev/null +++ b/renderdoc/common/shader_cache.h @@ -0,0 +1,174 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2015 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + + +#pragma once + +#include "os/os_specific.h" + +template +bool LoadShaderCache(const char *filename, + const uint32_t magicNumber, const uint32_t versionNumber, + std::map &resultCache, + const ShaderCallbacks &callbacks) +{ + string shadercache = FileIO::GetAppFolderFilename(filename); + + FILE *f = FileIO::fopen(shadercache.c_str(), "rb"); + + if(!f) + return false; + + FileIO::fseek64(f, 0, SEEK_END); + uint64_t cachelen = FileIO::ftell64(f); + FileIO::fseek64(f, 0, SEEK_SET); + + bool ret = true; + + // header: magic number, file version, number of entries + if(cachelen < sizeof(uint32_t)*3) + { + RDCERR("Invalid shader cache"); + ret = false; + } + else + { + byte *cache = new byte[(size_t)cachelen]; + FileIO::fread(cache, 1, (size_t)cachelen, f); + + uint32_t *header = (uint32_t *)cache; + + uint32_t fileMagic = header[0]; + uint32_t fileVer = header[1]; + + if(fileMagic != magicNumber || fileVer != versionNumber) + { + RDCDEBUG("Out of date or invalid shader cache magic: %d version: %d", fileMagic, fileVer); + ret = false; + } + else + { + uint32_t numentries = header[2]; + + byte *ptr = cache+sizeof(uint32_t)*3; + + int64_t bufsize = (int64_t)cachelen-sizeof(uint32_t)*3; + + for(uint32_t i=0; i < numentries; i++) + { + if(bufsize < sizeof(uint32_t)) + { + RDCERR("Invalid shader cache - truncated, not enough data for shader hash"); + ret = false; + break; + } + + uint32_t hash = *(uint32_t *)ptr; ptr += sizeof(uint32_t); bufsize -= sizeof(uint32_t); + + if(bufsize < sizeof(uint32_t)) + { + RDCERR("Invalid shader cache - truncated, not enough data for shader length"); + ret = false; + break; + } + + uint32_t len = *(uint32_t *)ptr; ptr += sizeof(uint32_t); bufsize -= sizeof(uint32_t); + + if(bufsize < len) + { + RDCERR("Invalid shader cache - truncated, not enough data for shader buffer"); + ret = false; + break; + } + + byte *data = ptr; ptr += len; bufsize -= len; + + ResultType result; + bool created = callbacks.Create(len, data, &result); + + if(!created) + { + RDCERR("Couldn't create blob of size %u from shadercache", len); + ret = false; + break; + } + + resultCache[hash] = result; + } + + if(ret == true && bufsize != 0) + { + RDCERR("Invalid shader cache - trailing data"); + ret = false; + } + + RDCDEBUG("Successfully loaded %d shaders from shader cache", resultCache.size()); + } + + delete[] cache; + } + + FileIO::fclose(f); + + return ret; +} + +template +void SaveShaderCache(const char *filename, + uint32_t magicNumber, uint32_t versionNumber, + const std::map &cache, + const ShaderCallbacks &callbacks) +{ + string shadercache = FileIO::GetAppFolderFilename(filename); + + FILE *f = FileIO::fopen(shadercache.c_str(), "wb"); + + if(!f) + { + RDCERR("Error opening shader cache for write"); + return; + } + + FileIO::fwrite(&magicNumber, 1, sizeof(magicNumber), f); + FileIO::fwrite(&versionNumber, 1, sizeof(versionNumber), f); + uint32_t numentries = (uint32_t)cache.size(); + FileIO::fwrite(&numentries, 1, sizeof(numentries), f); + + auto it = cache.begin(); + for(auto it = cache.begin(); it != cache.end(); ++it) + { + uint32_t hash = it->first; + uint32_t len = callbacks.GetSize(it->second); + byte *data = callbacks.GetData(it->second); + FileIO::fwrite(&hash, 1, sizeof(hash), f); + FileIO::fwrite(&len, 1, sizeof(len), f); + FileIO::fwrite(data, 1, len, f); + + callbacks.Destroy(it->second); + } + + FileIO::fclose(f); + + RDCDEBUG("Successfully wrote %u shaders to shader cache", numentries); +} diff --git a/renderdoc/driver/d3d11/d3d11_debug.cpp b/renderdoc/driver/d3d11/d3d11_debug.cpp index e790eb096..e715f78ab 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.cpp +++ b/renderdoc/driver/d3d11/d3d11_debug.cpp @@ -33,6 +33,8 @@ #include "serialise/string_utils.h" #include "maths/formatpacking.h" +#include "common/shader_cache.h" + #include "driver/d3d11/d3d11_resources.h" #include "d3d11_renderstate.h" @@ -95,6 +97,59 @@ DXGI_FORMAT GetTypedFormatUIntPreferred(DXGI_FORMAT f) return f; } +typedef HRESULT (WINAPI *pD3DCreateBlob)(SIZE_T Size, ID3DBlob** ppBlob); + +struct D3DBlobShaderCallbacks +{ + D3DBlobShaderCallbacks() + { + HMODULE d3dcompiler = GetD3DCompiler(); + + if(d3dcompiler == NULL) + RDCFATAL("Can't get handle to d3dcompiler_??.dll"); + + m_BlobCreate = (pD3DCreateBlob)GetProcAddress(d3dcompiler, "D3DCreateBlob"); + + if(m_BlobCreate == NULL) + RDCFATAL("d3dcompiler.dll doesn't contain D3DCreateBlob"); + } + + bool Create(uint32_t size, byte *data, ID3DBlob **ret) const + { + RDCASSERT(ret); + + *ret = NULL; + HRESULT hr = m_BlobCreate((SIZE_T)size, ret); + + if(FAILED(hr)) + { + RDCERR("Couldn't create blob of size %u from shadercache: %08x", size, hr); + return false; + } + + memcpy((*ret)->GetBufferPointer(), data, size); + + return true; + } + + void Destroy(ID3DBlob *blob) const + { + blob->Release(); + } + + uint32_t GetSize(ID3DBlob *blob) const + { + return (uint32_t)blob->GetBufferSize(); + } + + byte *GetData(ID3DBlob *blob) const + { + return (byte *)blob->GetBufferPointer(); + } + + pD3DCreateBlob m_BlobCreate; +} ShaderCacheCallbacks; + D3D11DebugManager::D3D11DebugManager(WrappedID3D11Device *wrapper) { if(RenderDoc::Inst().GetCrashHandler()) @@ -152,119 +207,10 @@ D3D11DebugManager::D3D11DebugManager(WrappedID3D11Device *wrapper) } } - string shadercache = FileIO::GetAppFolderFilename("shaders.cache"); + bool success = LoadShaderCache("d3dshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion, m_ShaderCache, ShaderCacheCallbacks); - m_ShaderCacheDirty = true; - - FILE *f = FileIO::fopen(shadercache.c_str(), "rb"); - if(f) - { - FileIO::fseek64(f, 0, SEEK_END); - uint64_t cachelen = FileIO::ftell64(f); - FileIO::fseek64(f, 0, SEEK_SET); - - if(cachelen < 8) - { - RDCERR("Invalid shader cache"); - m_ShaderCacheDirty = true; - } - else - { - byte *cache = new byte[(size_t)cachelen]; - FileIO::fread(cache, 1, (size_t)cachelen, f); - - uint32_t *header = (uint32_t *)cache; - - uint32_t version = header[0]; - - if(version != m_ShaderCacheVersion) - { - RDCDEBUG("Out of date or invalid shader cache version: %d", version); - m_ShaderCacheDirty = true; - } - else - { - uint32_t numentries = header[1]; - - byte *ptr = cache+sizeof(uint32_t)*2; - - int64_t bufsize = (int64_t)cachelen-sizeof(uint32_t)*2; - - HMODULE d3dcompiler = GetD3DCompiler(); - - if(d3dcompiler == NULL) - { - RDCFATAL("Can't get handle to d3dcompiler_??.dll"); - } - - typedef HRESULT (WINAPI *pD3DCreateBlob)(SIZE_T Size, ID3DBlob** ppBlob); - - pD3DCreateBlob blobCreate = (pD3DCreateBlob)GetProcAddress(d3dcompiler, "D3DCreateBlob"); - - if(blobCreate == NULL) - { - RDCFATAL("Can't get D3DCreateBlob from d3dcompiler_??.dll"); - } - - m_ShaderCacheDirty = false; - - for(uint32_t i=0; i < numentries; i++) - { - if(bufsize < sizeof(uint32_t)) - { - RDCERR("Invalid shader cache"); - m_ShaderCacheDirty = true; - break; - } - - uint32_t hash = *(uint32_t *)ptr; ptr += sizeof(uint32_t); bufsize -= sizeof(uint32_t); - - if(bufsize < sizeof(uint32_t)) - { - RDCERR("Invalid shader cache"); - m_ShaderCacheDirty = true; - break; - } - - uint32_t len = *(uint32_t *)ptr; ptr += sizeof(uint32_t); bufsize -= sizeof(uint32_t); - - if(bufsize < len) - { - RDCERR("Invalid shader cache"); - m_ShaderCacheDirty = true; - break; - } - - byte *data = ptr; ptr += len; bufsize -= len; - - ID3DBlob *blob = NULL; - hr = blobCreate((SIZE_T)len, &blob); - - if(FAILED(hr)) - { - RDCERR("Couldn't create blob of size %d from shadercache: %08x", len, hr); - m_ShaderCacheDirty = true; - } - - memcpy(blob->GetBufferPointer(), data, len); - - m_ShaderCache[hash] = blob; - } - - if(bufsize != 0) - { - RDCERR("Invalid shader cache"); - m_ShaderCacheDirty = true; - } - - RDCDEBUG("Successfully loaded %d shaders from shader cache", m_ShaderCache.size()); - } - - delete[] cache; - } - - fclose(f); - } + // if we failed to load from the cache + m_ShaderCacheDirty = !success; m_CacheShaders = true; @@ -284,39 +230,7 @@ D3D11DebugManager::~D3D11DebugManager() PreDeviceShutdownCounters(); if(m_ShaderCacheDirty) - { - string shadercache = FileIO::GetAppFolderFilename("shaders.cache"); - - FILE *f = FileIO::fopen(shadercache.c_str(), "wb"); - if(f) - { - uint32_t version = m_ShaderCacheVersion; - FileIO::fwrite(&version, 1, sizeof(version), f); - uint32_t numentries = (uint32_t)m_ShaderCache.size(); - FileIO::fwrite(&numentries, 1, sizeof(numentries), f); - - auto it = m_ShaderCache.begin(); - for(uint32_t i=0; i < numentries; i++) - { - uint32_t hash = it->first; - uint32_t len = (uint32_t)it->second->GetBufferSize(); - FileIO::fwrite(&hash, 1, sizeof(hash), f); - FileIO::fwrite(&len, 1, sizeof(len), f); - FileIO::fwrite(it->second->GetBufferPointer(), 1, len, f); - - it->second->Release(); - ++it; - } - - RDCDEBUG("Successfully wrote %d shaders to shader cache", m_ShaderCache.size()); - - fclose(f); - } - else - { - RDCERR("Error opening shader cache for write"); - } - } + SaveShaderCache("d3dshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion, m_ShaderCache, ShaderCacheCallbacks); ShutdownFontRendering(); ShutdownStreamOut(); diff --git a/renderdoc/driver/d3d11/d3d11_debug.h b/renderdoc/driver/d3d11/d3d11_debug.h index 74511021b..bc4495980 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.h +++ b/renderdoc/driver/d3d11/d3d11_debug.h @@ -294,7 +294,9 @@ class D3D11DebugManager uint64_t m_OutputWindowID; map m_OutputWindows; - static const uint32_t m_ShaderCacheVersion = 2; + static const uint32_t m_ShaderCacheMagic = 0xf000baba; + static const uint32_t m_ShaderCacheVersion = 3; + bool m_ShaderCacheDirty, m_CacheShaders; map m_ShaderCache; diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index c5c74c472..5c1a395aa 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -241,6 +241,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index c4a8a06e5..cd6c0108b 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -228,6 +228,9 @@ API\Replay + + Common +