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(), &macros.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();
}
Ejemplo n.º 2
0
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, &macros.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();
}