static string GetExpandedShaderCode(const wchar* path, GrowableList<wstring>& filePaths) { for(uint64 i = 0; i < filePaths.Count(); ++i) if(filePaths[i] == path) return string(); filePaths.Add(path); string fileContents = ReadFileAsString(path); // Look for includes size_t lineStart = 0; while(true) { size_t lineEnd = fileContents.find('\n', lineStart); size_t lineLength = 0; if(lineEnd == string::npos) lineLength = string::npos; else lineLength = lineEnd - lineStart; string line = fileContents.substr(lineStart, lineLength); if(line.find("#include") == 0) { wstring fullIncludePath; size_t startQuote = line.find('\"'); if(startQuote != -1) { size_t endQuote = line.find('\"', startQuote + 1); string includePath = line.substr(startQuote + 1, endQuote - startQuote - 1); fullIncludePath = AnsiToWString(includePath.c_str()); } else { startQuote = line.find('<'); if(startQuote == -1) throw Exception(L"Malformed include statement: \"" + AnsiToWString(line.c_str()) + L"\" in file " + path); size_t endQuote = line.find('>', startQuote + 1); string includePath = line.substr(startQuote + 1, endQuote - startQuote - 1); fullIncludePath = SampleFrameworkDir() + L"Shaders\\" + AnsiToWString(includePath.c_str()); } if(FileExists(fullIncludePath.c_str()) == false) throw Exception(L"Couldn't find #included file \"" + fullIncludePath + L"\" in file " + path); string includeCode = GetExpandedShaderCode(fullIncludePath.c_str(), filePaths); fileContents.insert(lineEnd + 1, includeCode); lineEnd += includeCode.length(); } if(lineEnd == string::npos) break; lineStart = lineEnd + 1; } return fileContents; }
static string GetExpandedShaderCode(const wchar* path, vector<wstring>& filePaths) { for(uint64 i = 0; i < filePaths.size(); ++i) if(filePaths[i] == path) throw Exception(L"File \"" + wstring(path) + L" is recursively included"); filePaths.push_back(path); string fileContents = ReadFileAsString(path); wstring fileDirectory = GetDirectoryFromFilePath(path); // Look for includes size_t lineStart = 0; size_t lineEnd = std::string::npos; while(true) { size_t lineEnd = fileContents.find('\n', lineStart); size_t lineLength = 0; if(lineEnd == string::npos) lineLength = string::npos; else lineLength = lineEnd - lineStart; string line = fileContents.substr(lineStart, lineLength); if(line.find("#include") == 0) { size_t startQuote = line.find('\"'); size_t endQuote = line.find('\"', startQuote + 1); string includePath = line.substr(startQuote + 1, endQuote - startQuote - 1); wstring fullIncludePath = fileDirectory + AnsiToWString(includePath.c_str()); if(FileExists(fullIncludePath.c_str()) == false) throw Exception(L"Couldn't find #included file \"" + fullIncludePath + L"\""); string includeCode = GetExpandedShaderCode(fullIncludePath.c_str(), filePaths); fileContents.insert(lineEnd + 1, includeCode); lineEnd += includeCode.length(); } if(lineEnd == string::npos) break; lineStart = lineEnd + 1; } return fileContents; }
static ID3DBlob* CompileShader(const wchar* path, const char* functionName, const char* profile, const D3D_SHADER_MACRO* defines, bool forceOptimization, vector<wstring>& filePaths) { // Make a hash off the expanded shader code string shaderCode = GetExpandedShaderCode(path, filePaths); wstring cacheName = MakeShaderCacheName(shaderCode, functionName, profile, defines); if(FileExists(cacheName.c_str())) { File cacheFile(cacheName.c_str(), File::OpenRead); const uint64 shaderSize = cacheFile.Size(); vector<uint8> compressedShader; compressedShader.resize(shaderSize); cacheFile.Read(shaderSize, compressedShader.data()); ID3DBlob* decompressedShader[1] = { nullptr }; uint32 indices[1] = { 0 }; DXCall(D3DDecompressShaders(compressedShader.data(), shaderSize, 1, 0, indices, 0, decompressedShader, nullptr)); return decompressedShader[0]; } std::printf("Compiling shader %s %s %s\n", WStringToAnsi(GetFileName(path).c_str()).c_str(), profile, MakeDefinesString(defines).c_str()); // Loop until we succeed, or an exception is thrown while(true) { UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS; #ifdef _DEBUG flags |= D3DCOMPILE_DEBUG; if(forceOptimization == false) flags |= D3DCOMPILE_SKIP_OPTIMIZATION; #endif ID3DBlob* compiledShader; ID3DBlobPtr errorMessages; HRESULT hr = D3DCompileFromFile(path, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, functionName, profile, flags, 0, &compiledShader, &errorMessages); if(FAILED(hr)) { if(errorMessages) { wchar message[1024] = { 0 }; char* blobdata = reinterpret_cast<char*>(errorMessages->GetBufferPointer()); MultiByteToWideChar(CP_ACP, 0, blobdata, static_cast<int>(errorMessages->GetBufferSize()), message, 1024); std::wstring fullMessage = L"Error compiling shader file \""; fullMessage += path; fullMessage += L"\" - "; fullMessage += message; // Pop up a message box allowing user to retry compilation int retVal = MessageBoxW(nullptr, fullMessage.c_str(), L"Shader Compilation Error", MB_RETRYCANCEL); if(retVal != IDRETRY) throw DXException(hr, fullMessage.c_str()); #if EnableShaderCaching_ shaderCode = GetExpandedShaderCode(path); cacheName = MakeShaderCacheName(shaderCode, functionName, profile, defines); #endif } else { _ASSERT(false); throw DXException(hr); } } else { // Compress the shader D3D_SHADER_DATA shaderData; shaderData.pBytecode = compiledShader->GetBufferPointer(); shaderData.BytecodeLength = compiledShader->GetBufferSize(); ID3DBlobPtr compressedShader; DXCall(D3DCompressShaders(1, &shaderData, D3D_COMPRESS_SHADER_KEEP_ALL_PARTS, &compressedShader)); // Create the cache directory if it doesn't exist if(DirectoryExists(baseCacheDir.c_str()) == false) Win32Call(CreateDirectory(baseCacheDir.c_str(), nullptr)); if(DirectoryExists(cacheDir.c_str()) == false) Win32Call(CreateDirectory(cacheDir.c_str(), nullptr)); File cacheFile(cacheName.c_str(), File::OpenWrite); // Write the compiled shader to disk uint64 shaderSize = compressedShader->GetBufferSize(); cacheFile.Write(shaderSize, compressedShader->GetBufferPointer()); return compiledShader; } } }
static ID3DBlob* CompileShader(const wchar* path, const char* functionName, ShaderType type, ShaderProfile profile, const D3D_SHADER_MACRO* defines, bool forceOptimization, GrowableList<wstring>& filePaths) { if(FileExists(path) == false) { Assert_(false); throw Exception(L"Shader file " + std::wstring(path) + L" does not exist"); } uint64 profileIdx = uint64(profile) * uint64(ShaderType::NumTypes) + uint64(type); Assert_(profileIdx < TotalNumProfiles); const char* profileString = ProfileStrings[profileIdx]; // Make a hash off the expanded shader code string shaderCode = GetExpandedShaderCode(path, filePaths); wstring cacheName = MakeShaderCacheName(shaderCode, functionName, profileString, defines); if(FileExists(cacheName.c_str())) { File cacheFile(cacheName.c_str(), FileOpenMode::Read); const uint64 shaderSize = cacheFile.Size(); Array<uint8> compressedShader; compressedShader.Init(shaderSize); cacheFile.Read(shaderSize, compressedShader.Data()); ID3DBlob* decompressedShader[1] = { nullptr }; uint32 indices[1] = { 0 }; DXCall(D3DDecompressShaders(compressedShader.Data(), shaderSize, 1, 0, indices, 0, decompressedShader, nullptr)); return decompressedShader[0]; } WriteLog("Compiling %s shader %s_%s %s\n", TypeStrings[uint64(type)], WStringToAnsi(GetFileName(path).c_str()).c_str(), functionName, MakeDefinesString(defines).c_str()); // Loop until we succeed, or an exception is thrown while(true) { UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS; #ifdef _DEBUG flags |= D3DCOMPILE_DEBUG; // This is causing some shader bugs /*if(forceOptimization == false) flags |= D3DCOMPILE_SKIP_OPTIMIZATION;*/ #endif ID3DBlob* compiledShader; ID3DBlobPtr errorMessages; FrameworkInclude include; HRESULT hr = D3DCompileFromFile(path, defines, &include, functionName, profileString, flags, 0, &compiledShader, &errorMessages); if(FAILED(hr)) { if(errorMessages) { wchar message[1024] = { 0 }; char* blobdata = reinterpret_cast<char*>(errorMessages->GetBufferPointer()); MultiByteToWideChar(CP_ACP, 0, blobdata, static_cast<int>(errorMessages->GetBufferSize()), message, 1024); std::wstring fullMessage = L"Error compiling shader file \""; fullMessage += path; fullMessage += L"\" - "; fullMessage += message; // Pop up a message box allowing user to retry compilation int retVal = MessageBoxW(nullptr, fullMessage.c_str(), L"Shader Compilation Error", MB_RETRYCANCEL); if(retVal != IDRETRY) throw DXException(hr, fullMessage.c_str()); } else { Assert_(false); throw DXException(hr); } } else { // Compress the shader D3D_SHADER_DATA shaderData; shaderData.pBytecode = compiledShader->GetBufferPointer(); shaderData.BytecodeLength = compiledShader->GetBufferSize(); ID3DBlobPtr compressedShader; DXCall(D3DCompressShaders(1, &shaderData, D3D_COMPRESS_SHADER_KEEP_ALL_PARTS, &compressedShader)); // Create the cache directory if it doesn't exist if(DirectoryExists(baseCacheDir.c_str()) == false) Win32Call(CreateDirectory(baseCacheDir.c_str(), nullptr)); if(DirectoryExists(cacheDir.c_str()) == false) Win32Call(CreateDirectory(cacheDir.c_str(), nullptr)); File cacheFile(cacheName.c_str(), FileOpenMode::Write); // Write the compiled shader to disk uint64 shaderSize = compressedShader->GetBufferSize(); cacheFile.Write(shaderSize, compressedShader->GetBufferPointer()); return compiledShader; } } }