mirror of
				https://github.com/hedge-dev/XenosRecomp.git
				synced 2025-10-30 07:12:17 +00:00 
			
		
		
		
	Save compressed shader cache to a .cpp file.
This commit is contained in:
		
							parent
							
								
									9da6b59ce5
								
							
						
					
					
						commit
						b152142709
					
				
					 5 changed files with 141 additions and 83 deletions
				
			
		|  | @ -17,11 +17,15 @@ add_executable(ShaderRecomp | ||||||
| 
 | 
 | ||||||
| find_package(directx-dxc CONFIG REQUIRED) | find_package(directx-dxc CONFIG REQUIRED) | ||||||
| find_package(xxhash CONFIG REQUIRED) | find_package(xxhash CONFIG REQUIRED) | ||||||
|  | find_package(zstd CONFIG REQUIRED) | ||||||
| 
 | 
 | ||||||
| target_link_libraries(ShaderRecomp PRIVATE | target_link_libraries(ShaderRecomp PRIVATE | ||||||
|     Microsoft::DirectXShaderCompiler |     Microsoft::DirectXShaderCompiler | ||||||
|     xxHash::xxhash) |     xxHash::xxhash | ||||||
|  |     $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>) | ||||||
| 
 | 
 | ||||||
| target_precompile_headers(ShaderRecomp PRIVATE pch.h) | target_precompile_headers(ShaderRecomp PRIVATE pch.h) | ||||||
| 
 | 
 | ||||||
| add_compile_definitions(ShaderRecomp _CRT_SECURE_NO_WARNINGS) | add_compile_definitions(ShaderRecomp _CRT_SECURE_NO_WARNINGS) | ||||||
|  | 
 | ||||||
|  | file(COPY ${PACKAGE_PREFIX_DIR}/bin/dxil.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) | ||||||
|  |  | ||||||
|  | @ -21,110 +21,157 @@ static void writeAllBytes(const char* filePath, const void* data, size_t dataSiz | ||||||
|     fclose(file); |     fclose(file); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct ShaderCacheEntry | struct RecompiledShader | ||||||
| { | { | ||||||
|     XXH64_hash_t hash = 0; |     uint8_t* data = nullptr; | ||||||
|     uint32_t dxilOffset = 0; |  | ||||||
|     uint32_t dxilSize = 0; |  | ||||||
|     uint32_t spirvOffset = 0; |  | ||||||
|     uint32_t spirvSize = 0; |  | ||||||
|     uint64_t reserved = 0; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct ShaderHolder |  | ||||||
| { |  | ||||||
|     std::string filePath; |  | ||||||
|     size_t fileSize = 0; |  | ||||||
|     std::unique_ptr<uint8_t[]> fileData; |  | ||||||
|     IDxcBlob* dxil = nullptr; |     IDxcBlob* dxil = nullptr; | ||||||
|     IDxcBlob* spirv = nullptr; |     IDxcBlob* spirv = nullptr; | ||||||
|     ShaderCacheEntry entry; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct ShaderCacheHeader |  | ||||||
| { |  | ||||||
|     uint32_t version = 0; |  | ||||||
|     uint32_t count = 0; |  | ||||||
|     uint32_t reserved0 = 0; |  | ||||||
|     uint32_t reserved1 = 0; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| int main(int argc, char** argv) | int main(int argc, char** argv) | ||||||
| { | { | ||||||
|     if (std::filesystem::is_directory(argv[1])) |     const char* input = | ||||||
|  | #ifdef SHADER_RECOMP_INPUT  | ||||||
|  |         SHADER_RECOMP_INPUT | ||||||
|  | #else | ||||||
|  |         argv[1] | ||||||
|  | #endif | ||||||
|  |     ; | ||||||
|  | 
 | ||||||
|  |     const char* output = | ||||||
|  | #ifdef SHADER_RECOMP_OUTPUT  | ||||||
|  |         SHADER_RECOMP_OUTPUT | ||||||
|  | #else | ||||||
|  |         argv[2] | ||||||
|  | #endif | ||||||
|  |         ; | ||||||
|  | 
 | ||||||
|  |     if (std::filesystem::is_directory(input)) | ||||||
|     { |     { | ||||||
|         std::map<XXH64_hash_t, ShaderHolder> shaders; |         std::vector<std::unique_ptr<uint8_t[]>> files; | ||||||
|  |         std::map<XXH64_hash_t, RecompiledShader> shaders; | ||||||
| 
 | 
 | ||||||
|         for (auto& file : std::filesystem::directory_iterator(argv[1])) |         for (auto& file : std::filesystem::directory_iterator(input)) | ||||||
|         { |         { | ||||||
|             auto extension = file.path().extension(); |             size_t fileSize = 0; | ||||||
|             if (extension == ".xpu" || extension == ".xvu") |             auto fileData = readAllBytes(file.path().string().c_str(), fileSize); | ||||||
|             { |             bool foundAny = false; | ||||||
|                 ShaderHolder shader; |  | ||||||
|                 shader.filePath = file.path().string(); |  | ||||||
|                 shader.fileData = readAllBytes(shader.filePath.c_str(), shader.fileSize); |  | ||||||
|                 shader.entry.hash = XXH3_64bits(shader.fileData.get(), shader.fileSize); |  | ||||||
| 
 | 
 | ||||||
|                 shaders.emplace(shader.entry.hash, std::move(shader)); |             for (size_t i = 0; fileSize > sizeof(ShaderContainer) && i < fileSize - sizeof(ShaderContainer) - 1;) | ||||||
|  |             { | ||||||
|  |                 auto shaderContainer = reinterpret_cast<const ShaderContainer*>(fileData.get() + i); | ||||||
|  |                 size_t dataSize = shaderContainer->virtualSize + shaderContainer->physicalSize; | ||||||
|  | 
 | ||||||
|  |                 if ((shaderContainer->flags & 0xFFFFFF00) == 0x102A1100 && | ||||||
|  |                     dataSize < (fileSize - i) && | ||||||
|  |                     shaderContainer->field1C == 0 && | ||||||
|  |                     shaderContainer->field20 == 0) | ||||||
|  |                 { | ||||||
|  |                     XXH64_hash_t hash = XXH3_64bits(shaderContainer, dataSize); | ||||||
|  |                     auto shader = shaders.try_emplace(hash); | ||||||
|  |                     if (shader.second) | ||||||
|  |                     { | ||||||
|  |                         shader.first->second.data = fileData.get() + i; | ||||||
|  |                         foundAny = true; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     i += dataSize; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     i += sizeof(uint32_t); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             if (foundAny) | ||||||
|  |                 files.emplace_back(std::move(fileData)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         std::atomic<uint32_t> progress = 0; | ||||||
|  | 
 | ||||||
|         std::for_each(std::execution::par_unseq, shaders.begin(), shaders.end(), [&](auto& hashShaderPair) |         std::for_each(std::execution::par_unseq, shaders.begin(), shaders.end(), [&](auto& hashShaderPair) | ||||||
|             { |             { | ||||||
|                 auto& shader = hashShaderPair.second; |                 auto& shader = hashShaderPair.second; | ||||||
|                 printf("%s\n", shader.filePath.c_str()); |  | ||||||
| 
 | 
 | ||||||
|                 thread_local ShaderRecompiler recompiler; |                 thread_local ShaderRecompiler recompiler; | ||||||
|                 recompiler = {}; |                 recompiler = {}; | ||||||
|                 recompiler.recompile(shader.fileData.get()); |                 recompiler.recompile(shader.data); | ||||||
|                 writeAllBytes((shader.filePath + ".hlsl").c_str(), recompiler.out.data(), recompiler.out.size()); |  | ||||||
| 
 | 
 | ||||||
|                 thread_local DxcCompiler dxcCompiler; |                 thread_local DxcCompiler dxcCompiler; | ||||||
| 
 |  | ||||||
|                 shader.dxil = dxcCompiler.compile(recompiler.out, recompiler.isPixelShader, false); |                 shader.dxil = dxcCompiler.compile(recompiler.out, recompiler.isPixelShader, false); | ||||||
|                 shader.spirv = dxcCompiler.compile(recompiler.out, recompiler.isPixelShader, true); |                 shader.spirv = dxcCompiler.compile(recompiler.out, recompiler.isPixelShader, true); | ||||||
| 
 | 
 | ||||||
|                 assert(shader.dxil != nullptr && shader.spirv != nullptr); |                 assert(shader.dxil != nullptr && shader.spirv != nullptr); | ||||||
|                 assert(*(reinterpret_cast<uint32_t*>(shader.dxil->GetBufferPointer()) + 1) != 0 && "DXIL was not signed properly!"); |                 assert(*(reinterpret_cast<uint32_t*>(shader.dxil->GetBufferPointer()) + 1) != 0 && "DXIL was not signed properly!"); | ||||||
|  | 
 | ||||||
|  |                 size_t currentProgress = ++progress; | ||||||
|  |                 if ((currentProgress % 10) == 0 || (currentProgress == shaders.size() - 1)) | ||||||
|  |                     std::println("Recompiling shaders... {}%", currentProgress / float(shaders.size()) * 100.0f); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         FILE* file = fopen(argv[2], "wb"); |         std::println("Creating shader cache..."); | ||||||
| 
 | 
 | ||||||
|         ShaderCacheHeader header{}; |         StringBuffer f; | ||||||
|         header.version = 0; |         f.println("#include \"shader_cache.h\""); | ||||||
|         header.count = shaders.size(); |         f.println("ShaderCacheEntry g_shaderCacheEntries[] = {{"); | ||||||
|         fwrite(&header, sizeof(header), 1, file); |  | ||||||
| 
 | 
 | ||||||
|         uint32_t headerSize = sizeof(ShaderCacheHeader) + shaders.size() * sizeof(ShaderCacheEntry); |         std::vector<uint8_t> dxil; | ||||||
|         uint32_t dxilOffset = headerSize; |         std::vector<uint8_t> spirv; | ||||||
|         uint32_t spirvOffset = 0; |  | ||||||
| 
 | 
 | ||||||
|         for (auto& [_, shader] : shaders) |         for (auto& [hash, shader] : shaders) | ||||||
|         { |         { | ||||||
|             shader.entry.dxilOffset = dxilOffset; |             f.println("\t{{ 0x{:X}, {}, {}, {}, {} }},", | ||||||
|             shader.entry.dxilSize = uint32_t(shader.dxil->GetBufferSize()); |                 hash, dxil.size(), shader.dxil->GetBufferSize(), spirv.size(), shader.spirv->GetBufferSize()); | ||||||
|             shader.entry.spirvOffset = spirvOffset; | 
 | ||||||
|             shader.entry.spirvSize = uint32_t(shader.spirv->GetBufferSize()); |             dxil.insert(dxil.end(), reinterpret_cast<uint8_t*>(shader.dxil->GetBufferPointer()), | ||||||
|             dxilOffset += shader.entry.dxilSize; |                 reinterpret_cast<uint8_t*>(shader.dxil->GetBufferPointer()) + shader.dxil->GetBufferSize());     | ||||||
|             spirvOffset += shader.entry.spirvSize; |              | ||||||
|  |             spirv.insert(spirv.end(), reinterpret_cast<uint8_t*>(shader.spirv->GetBufferPointer()), | ||||||
|  |                 reinterpret_cast<uint8_t*>(shader.spirv->GetBufferPointer()) + shader.spirv->GetBufferSize()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (auto& [_, shader] : shaders) |         f.println("}};"); | ||||||
|         { |  | ||||||
|             shader.entry.spirvOffset += dxilOffset; |  | ||||||
|             fwrite(&shader.entry, sizeof(shader.entry), 1, file); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         for (auto& [_, shader] : shaders) |         std::println("Compressing DXIL cache..."); | ||||||
|             fwrite(shader.dxil->GetBufferPointer(), 1, shader.dxil->GetBufferSize(), file); |  | ||||||
| 
 | 
 | ||||||
|         for (auto& [_, shader] : shaders) |         int level = ZSTD_maxCLevel(); | ||||||
|             fwrite(shader.spirv->GetBufferPointer(), 1, shader.spirv->GetBufferSize(), file); |         //level = ZSTD_defaultCLevel();
 | ||||||
|  |         std::vector<uint8_t> dxilCompressed(ZSTD_compressBound(dxil.size())); | ||||||
|  |         dxilCompressed.resize(ZSTD_compress(dxilCompressed.data(), dxilCompressed.size(), dxil.data(), dxil.size(), level)); | ||||||
| 
 | 
 | ||||||
|         fclose(file); |         f.print("uint8_t g_compressedDxilCache[] = {{"); | ||||||
|  | 
 | ||||||
|  |         for (auto data : dxilCompressed) | ||||||
|  |             f.print("{},", data); | ||||||
|  | 
 | ||||||
|  |         f.println("}};"); | ||||||
|  | 
 | ||||||
|  |         std::println("Compressing SPIRV cache..."); | ||||||
|  | 
 | ||||||
|  |         std::vector<uint8_t> spirvCompressed(ZSTD_compressBound(spirv.size())); | ||||||
|  |         spirvCompressed.resize(ZSTD_compress(spirvCompressed.data(), spirvCompressed.size(), spirv.data(), spirv.size(), level)); | ||||||
|  | 
 | ||||||
|  |         f.print("uint8_t g_compressedSpirvCache[] = {{"); | ||||||
|  | 
 | ||||||
|  |         for (auto data : spirvCompressed) | ||||||
|  |             f.print("{},", data); | ||||||
|  | 
 | ||||||
|  |         f.println("}};"); | ||||||
|  | 
 | ||||||
|  |         f.println("size_t g_shaderCacheEntryCount = {};", shaders.size()); | ||||||
|  |         f.println("size_t g_dxilCacheCompressedSize = {};", dxilCompressed.size()); | ||||||
|  |         f.println("size_t g_dxilCacheDecompressedSize = {};", dxil.size()); | ||||||
|  |         f.println("size_t g_spirvCacheCompressedSize = {};", spirvCompressed.size()); | ||||||
|  |         f.println("size_t g_spirvCacheDecompressedSize = {};", spirv.size()); | ||||||
|  | 
 | ||||||
|  |         writeAllBytes(output, f.out.data(), f.out.size()); | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|  |         ShaderRecompiler recompiler; | ||||||
|  |         size_t fileSize; | ||||||
|  |         recompiler.recompile(readAllBytes(input, fileSize).get()); | ||||||
|  |         writeAllBytes(output, recompiler.out.data(), recompiler.out.size()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
|  | @ -8,11 +8,13 @@ | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include <execution> | #include <execution> | ||||||
| #include <filesystem> | #include <filesystem> | ||||||
|  | #include <print> | ||||||
| #include <format> | #include <format> | ||||||
| #include <map> | #include <map> | ||||||
| #include <string> | #include <string> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <xxhash.h> | #include <xxhash.h> | ||||||
|  | #include <zstd.h> | ||||||
| 
 | 
 | ||||||
| template<typename T> | template<typename T> | ||||||
| struct be | struct be | ||||||
|  |  | ||||||
|  | @ -3,9 +3,26 @@ | ||||||
| #include "shader.h" | #include "shader.h" | ||||||
| #include "shader_code.h" | #include "shader_code.h" | ||||||
| 
 | 
 | ||||||
| struct ShaderRecompiler | struct StringBuffer | ||||||
| { | { | ||||||
|     std::string out; |     std::string out; | ||||||
|  | 
 | ||||||
|  |     template<class... Args> | ||||||
|  |     void print(std::format_string<Args...> fmt, Args&&... args) | ||||||
|  |     { | ||||||
|  |         std::vformat_to(std::back_inserter(out), fmt.get(), std::make_format_args(args...)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<class... Args> | ||||||
|  |     void println(std::format_string<Args...> fmt, Args&&... args) | ||||||
|  |     { | ||||||
|  |         std::vformat_to(std::back_inserter(out), fmt.get(), std::make_format_args(args...)); | ||||||
|  |         out += '\n'; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct ShaderRecompiler : StringBuffer | ||||||
|  | { | ||||||
|     uint32_t indentation = 0; |     uint32_t indentation = 0; | ||||||
|     bool isPixelShader = false; |     bool isPixelShader = false; | ||||||
|     const uint8_t* constantTableData = nullptr; |     const uint8_t* constantTableData = nullptr; | ||||||
|  | @ -22,19 +39,6 @@ struct ShaderRecompiler | ||||||
|             out += '\t'; |             out += '\t'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<class... Args> |  | ||||||
|     void print(std::format_string<Args...> fmt, Args&&... args) |  | ||||||
|     { |  | ||||||
|         std::vformat_to(std::back_inserter(out), fmt.get(), std::make_format_args(args...)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<class... Args> |  | ||||||
|     void println(std::format_string<Args...> fmt, Args&&... args) |  | ||||||
|     { |  | ||||||
|         std::vformat_to(std::back_inserter(out), fmt.get(), std::make_format_args(args...)); |  | ||||||
|         out += '\n'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void printDstSwizzle(uint32_t dstSwizzle, bool operand); |     void printDstSwizzle(uint32_t dstSwizzle, bool operand); | ||||||
|     void printDstSwizzle01(uint32_t dstRegister, uint32_t dstSwizzle); |     void printDstSwizzle01(uint32_t dstRegister, uint32_t dstSwizzle); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
|     "builtin-baseline": "e63bd09dc0b7204467705c1c7c71d0e2a3f8860b", |     "builtin-baseline": "e63bd09dc0b7204467705c1c7c71d0e2a3f8860b", | ||||||
|     "dependencies": [ |     "dependencies": [ | ||||||
|         "directx-dxc", |         "directx-dxc", | ||||||
|         "xxhash" |         "xxhash", | ||||||
|  |         "zstd" | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Skyth
						Skyth