bool ShaderVariation::Compile() { const String& sourceCode = owner_->GetSourceCode(type_); Vector<String> defines = defines_.Split(' '); // Set the entrypoint, profile and flags according to the shader being compiled const char* entryPoint = 0; const char* profile = 0; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (type_ == VS) { entryPoint = "VS"; defines.Push("COMPILEVS"); profile = "vs_3_0"; } else { entryPoint = "PS"; defines.Push("COMPILEPS"); profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } defines.Push("MAXBONES=" + String(Graphics::GetMaxBones())); // Collect defines into macros Vector<String> defineValues; PODVector<D3D_SHADER_MACRO> macros; for (unsigned i = 0; i < defines.Size(); ++i) { unsigned equalsPos = defines[i].Find('='); if (equalsPos != String::NPOS) { defineValues.Push(defines[i].Substring(equalsPos + 1)); defines[i].Resize(equalsPos); } else defineValues.Push("1"); } for (unsigned i = 0; i < defines.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines[i].CString(); macro.Definition = defineValues[i].CString(); macros.Push(macro); // In debug mode, check that all defines are referenced by the shader code #ifdef _DEBUG if (sourceCode.Find(defines[i]) == String::NPOS) ATOMIC_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]); #endif } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); // Compile using D3DCompile ID3DBlob* shaderCode = 0; ID3DBlob* errorMsgs = 0; HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), ¯os.Front(), 0, entryPoint, profile, flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { // Do not include end zero unnecessarily compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1); } else { if (type_ == VS) ATOMIC_LOGDEBUG("Compiled vertex shader " + GetFullName()); else ATOMIC_LOGDEBUG("Compiled pixel shader " + GetFullName()); // Inspect the produced bytecode using MojoShader, then strip and store it unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer(); unsigned bufSize = (unsigned)shaderCode->GetBufferSize(); ParseParameters(bufData, bufSize); CopyStrippedCode(byteCode_, bufData, bufSize); } ATOMIC_SAFE_RELEASE(shaderCode); ATOMIC_SAFE_RELEASE(errorMsgs); return !byteCode_.Empty(); }
void CompileVariation(CompiledVariation* variation) { IncludeHandler includeHandler; PODVector<D3D_SHADER_MACRO> macros; // Insert variation-specific and global defines for (unsigned i = 0; i < variation->defines_.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = variation->defines_[i].CString(); macro.Definition = variation->defineValues_[i].CString(); macros.Push(macro); } for (unsigned i = 0; i < defines_.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines_[i].CString(); macro.Definition = defineValues_[i].CString(); macros.Push(macro); } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); LPD3DBLOB shaderCode = NULL; LPD3DBLOB errorMsgs = NULL; // Set the profile, entrypoint and flags according to the shader being compiled String profile; String entryPoint; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (variation->type_ == VS) { entryPoint = "VS"; if (!useSM3_) profile = "vs_2_0"; else profile = "vs_3_0"; } else { entryPoint = "PS"; if (!useSM3_) profile = "ps_2_0"; else { profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } } // Compile using D3DCompiler HRESULT hr = D3DCompile(hlslCode_.CString(), hlslCode_.Length(), NULL, ¯os.Front(), &includeHandler, entryPoint.CString(), profile.CString(), flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { variation->errorMsg_ = String((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); compileFailed_ = true; } else { BYTE const *const bufData = static_cast<BYTE *>(shaderCode->GetBufferPointer()); SIZE_T const bufSize = shaderCode->GetBufferSize(); MOJOSHADER_parseData const *parseData = MOJOSHADER_parse("bytecode", bufData, bufSize, NULL, 0, NULL, 0, NULL, NULL, NULL); CopyStrippedCode(variation->byteCode_, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize()); if (!variation->name_.Empty()) PrintLine("Compiled shader variation " + variation->name_ + ", code size " + String(variation->byteCode_.Size())); else PrintLine("Compiled base shader variation, code size " + String(variation->byteCode_.Size())); // Print warnings if any if (errorMsgs && errorMsgs->GetBufferSize()) { String warning((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); PrintLine("WARNING: " + warning); } for(int i = 0; i < parseData->symbol_count; i++) { MOJOSHADER_symbol const& symbol = parseData->symbols[i]; String name(symbol.name); unsigned const reg = symbol.register_index; unsigned const regCount = symbol.register_count; // Check if the parameter is a constant or a texture sampler bool const isSampler = (name[0] == 's'); name = name.Substring(1); if (isSampler) { // Skip if it's a G-buffer sampler, which are aliases for the standard texture units if (name != "AlbedoBuffer" && name != "NormalBuffer" && name != "DepthBuffer" && name != "LightBuffer") { Parameter newTextureUnit(name, reg, 1); variation->textureUnits_.Push(newTextureUnit); } } else { Parameter newParam(name, reg, regCount); variation->constants_.Push(newParam); } } MOJOSHADER_freeParseData(parseData); // Create the last part of the output path (SM2/SM3) if it does not exist String outPath = GetPath(variation->outFileName_); if (!fileSystem_->DirExists(outPath)) fileSystem_->CreateDir(outPath); File outFile(context_); if (!outFile.Open(variation->outFileName_, FILE_WRITE)) { variation->errorMsg_ = "Could not open output file " + variation->outFileName_; compileFailed_ = true; } else { outFile.WriteFileID("USHD"); outFile.WriteShort((unsigned short)variation->type_); outFile.WriteShort(useSM3_ ? 3 : 2); outFile.WriteUInt(variation->constants_.Size()); for (unsigned i = 0; i < variation->constants_.Size(); ++i) { outFile.WriteString(variation->constants_[i].name_); outFile.WriteUByte(variation->constants_[i].register_); outFile.WriteUByte(variation->constants_[i].regCount_); } outFile.WriteUInt(variation->textureUnits_.Size()); for (unsigned i = 0; i < variation->textureUnits_.Size(); ++i) { outFile.WriteString(variation->textureUnits_[i].name_); outFile.WriteUByte(variation->textureUnits_[i].register_); } unsigned dataSize = variation->byteCode_.Size(); outFile.WriteUInt(dataSize); if (dataSize) outFile.Write(&variation->byteCode_[0], dataSize); } } if (shaderCode) shaderCode->Release(); if (errorMsgs) errorMsgs->Release(); }