#define INITGUID #include "ShaderD3D11.h" #include "GeometryD3D11.h" #include "DeviceD3D11.h" #include "HeadersD3D11.h" #include #include #include #include LOGGROUP(Graphics) namespace RBX { namespace Graphics { typedef HRESULT (WINAPI *TypeD3DCompile)(LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO *, ID3DInclude *, LPCSTR, LPCSTR, UINT, UINT, ID3DBlob **, ID3DBlob **); typedef HRESULT (WINAPI *TypeD3DPreprocess)(LPCVOID, SIZE_T, LPCSTR, const D3D_SHADER_MACRO *, ID3DInclude *, ID3DBlob **, ID3DBlob **); typedef HRESULT (WINAPI *TypeD3DReflect)(LPCVOID, SIZE_T, REFIID, void**); static TypeD3DCompile loadShaderCompiler() { #if !defined(RBX_PLATFORM_DURANGO) HMODULE d3dCompiler = ShaderProgramD3D11::loadShaderCompilerDLL(); return d3dCompiler ? (TypeD3DCompile)GetProcAddress(d3dCompiler, "D3DCompile") : NULL; #else return &D3DCompile; #endif } static TypeD3DPreprocess loadShaderPreprocessor() { #if !defined(RBX_PLATFORM_DURANGO) HMODULE d3dCompiler = ShaderProgramD3D11::loadShaderCompilerDLL(); return d3dCompiler ? (TypeD3DPreprocess)GetProcAddress(d3dCompiler, "D3DPreprocess") : NULL; #else return &D3DPreprocess; #endif } static TypeD3DReflect loadShaderReflector() { #if !defined(RBX_PLATFORM_DURANGO) HMODULE d3dCompiler = ShaderProgramD3D11::loadShaderCompilerDLL(); return d3dCompiler ? (TypeD3DReflect)GetProcAddress(d3dCompiler, "D3DReflect") : NULL; #else return &D3DReflect; #endif } static void extractCbuffers(Device* device, const std::vector& bytecode, std::vector>& cbuffers, unsigned globalSize, unsigned int* outSamplerMask) { TypeD3DReflect D3DReflect = loadShaderReflector(); RBXASSERT(D3DReflect); cbuffers.clear(); ID3D11ShaderReflection* shaderReflection11 = NULL; HRESULT hr = D3DReflect(bytecode.data(), bytecode.size(), IID_ID3D11ShaderReflection, (void**) &shaderReflection11); RBXASSERT(SUCCEEDED(hr)); D3D11_SHADER_DESC shaderDesc; shaderReflection11->GetDesc( &shaderDesc ); unsigned int samplerMask = 0; for (unsigned i = 0; i < shaderDesc.BoundResources; ++i) { D3D11_SHADER_INPUT_BIND_DESC desc; HRESULT hr =shaderReflection11->GetResourceBindingDesc (i, &desc); RBXASSERT(SUCCEEDED(hr)); if(desc.Type == D3D_SIT_TEXTURE) { samplerMask |= 1 << desc.BindPoint; } else if(desc.Type == D3D_SIT_CBUFFER) { ID3D11ShaderReflectionConstantBuffer* cb = shaderReflection11->GetConstantBufferByName(desc.Name); D3D11_SHADER_BUFFER_DESC cbDesc; cb->GetDesc( &cbDesc ); if (cbDesc.Type == D3D11_CT_CBUFFER) { if (desc.BindPoint == 0) { bool isGlobals = strcmp(cbDesc.Name, "Globals") == 0; if (!isGlobals) throw std::runtime_error("D3D11 Shader compilation: Globals are not bound to register 0 (or not bound at all)"); if (isGlobals && cbDesc.Size != globalSize) throw std::runtime_error("D3D11 Shader compilation: Globals CBuffer size is not the same as defined CBuffer size"); continue; // globals are same for all the shaders globally in deviceContext, so we want to skip it here } std::vector uniforms; for ( unsigned varId = 0; varId < cbDesc.Variables; ++varId ) { ID3D11ShaderReflectionVariable* var = cb->GetVariableByIndex( varId ); D3D11_SHADER_VARIABLE_DESC varDesc; var->GetDesc(&varDesc); ID3D11ShaderReflectionType* type = var->GetType(); D3D11_SHADER_TYPE_DESC typeDesc; type->GetDesc( &typeDesc ); UniformD3D11 uniform = UniformD3D11(); uniform.name = varDesc.Name; uniform.offset = varDesc.StartOffset; uniform.size = varDesc.Size; uniforms.push_back(uniform); } cbuffers.push_back(shared_ptr(new CBufferD3D11(device, desc.BindPoint, cbDesc.Name, cbDesc.Size, uniforms))); } } } if (outSamplerMask) *outSamplerMask = samplerMask; ReleaseCheck(shaderReflection11); } static int findCBuffer(const std::vector>& cBuffers, const std::string& name) { for(unsigned i = 0; i < cBuffers.size(); ++i) { if (cBuffers[i]->getName() == name) return i; } return -1; } static ID3D11VertexShader* createVertexShader(Device* device, const std::vector& bytecode, std::vector>& cbuffers, int& uniformsCBuffer, unsigned& maxWorldTransforms, int& worldMatCBuffer, int& uniformWorldMatrix, int& uniformWorldMatrixArray) { DeviceContextD3D11* context = static_cast(device)->getImmediateContextD3D11(); ID3D11Device* device11 = static_cast(device)->getDevice11(); extractCbuffers(device, bytecode, cbuffers, context->getGlobalDataSize(), NULL); uniformsCBuffer = findCBuffer(cbuffers, "$Globals"); worldMatCBuffer = findCBuffer(cbuffers, "WorldMatrixCB"); if (worldMatCBuffer >= 0) { uniformWorldMatrix = cbuffers[worldMatCBuffer]->findUniform("WorldMatrix"); if (uniformWorldMatrix >= 0) { maxWorldTransforms = 1; } uniformWorldMatrixArray = cbuffers[worldMatCBuffer]->findUniform("WorldMatrixArray"); if (uniformWorldMatrixArray >= 0) { const UniformD3D11& uniform = cbuffers[worldMatCBuffer]->getUniform(uniformWorldMatrixArray); maxWorldTransforms = uniform.size / (4 * 4 * 3); // 4bytes per float * 3 vectors } } ID3D11VertexShader* vertexShader = NULL; HRESULT hr = device11->CreateVertexShader( bytecode.data(), bytecode.size(), NULL, &vertexShader); RBXASSERT(SUCCEEDED(hr)); return vertexShader; } static ID3D11PixelShader* createPixelShader(Device* device, const std::vector& bytecode, std::vector>& cbuffers, int& uniformsCBuffer, unsigned int& samplerMask) { DeviceContextD3D11* context = static_cast(device)->getImmediateContextD3D11(); ID3D11Device* device11 = static_cast(device)->getDevice11(); extractCbuffers(device, bytecode, cbuffers, context->getGlobalDataSize(), &samplerMask); uniformsCBuffer = findCBuffer(cbuffers, "$Globals"); ID3D11PixelShader* pixelShader = NULL; HRESULT hr = device11->CreatePixelShader(bytecode.data(), bytecode.size(), NULL, &pixelShader); RBXASSERT(SUCCEEDED(hr)); return pixelShader; } const UniformD3D11& CBufferD3D11::getUniform(int id) const { RBXASSERT(id >= 0 && id < (int)uniforms.size()); return uniforms[id]; } CBufferD3D11::CBufferD3D11(Device* device, int registerId, const std::string& name, unsigned sizeIn, const std::vector& uniformsIn): Resource(device), registerId(registerId), name(name), size(sizeIn), data(NULL), dirty(true), uniforms(uniformsIn), object(NULL) { ID3D11Device* device11 = static_cast(device)->getDevice11(); data = new char[size]; memset(data, 0, size); D3D11_BUFFER_DESC cbDesc; cbDesc.Usage = D3D11_USAGE_DEFAULT; cbDesc.ByteWidth = size; cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbDesc.CPUAccessFlags = 0; cbDesc.MiscFlags = 0; cbDesc.StructureByteStride = 0; HRESULT hr = device11->CreateBuffer(&cbDesc, NULL, &object); RBXASSERT(SUCCEEDED(hr)); } void CBufferD3D11::updateUniform(int uniformId, const float* uniformData, unsigned vectorCount) { size_t uniformSize = vectorCount * sizeof(float) * 4; // our vectors are always float4 RBXASSERT(uniformId >= 0 && uniformId < (int)uniforms.size()); RBXASSERT(uniforms[uniformId].size >= uniformSize); if (dirty || memcmp(&data[uniforms[uniformId].offset], uniformData, uniformSize) != 0) { dirty = true; memcpy(&data[uniforms[uniformId].offset], uniformData, uniformSize); } } void CBufferD3D11::updateBuffer() { if (dirty) { ID3D11Device* device11 = static_cast(device)->getDevice11(); ID3D11DeviceContext* context11 = static_cast(device)->getImmediateContext11(); context11->UpdateSubresource(object, 0, NULL, data, 0, 0); dirty = false; } } int CBufferD3D11::findUniform(const std::string& uniformName) { for(unsigned i = 0; i < uniforms.size(); ++i) { if (uniforms[i].name == uniformName) return i; } return -1; } CBufferD3D11::~CBufferD3D11() { ReleaseCheck(object); delete[] data; } BaseShaderD3D11::BaseShaderD3D11(const std::vector& bytecode) : bytecode(bytecode) , uniformsBufferId(-1) { } int BaseShaderD3D11::findUniform(const std::string& name) { if (uniformsBufferId < 0) return -1; return cBuffers[uniformsBufferId]->findUniform(name); } void BaseShaderD3D11::setConstant(int handle, const float* data, size_t vectorCount) { if (uniformsBufferId < 0) return; cBuffers[uniformsBufferId]->updateUniform(handle, data, vectorCount); } void BaseShaderD3D11::updateConstantBuffers() { for (size_t i = 0; i < cBuffers.size(); ++i) cBuffers[i]->updateBuffer(); } VertexShaderD3D11::VertexShaderD3D11(Device* device, const std::vector& bytecode) : VertexShader(device) , BaseShaderD3D11(bytecode) , object(NULL) , worldMatrixCbuffer(-1) , worldMatrixArray(-1) , worldMatrix(-1) , maxWorldTransforms(0) { object = createVertexShader(device, bytecode, cBuffers, uniformsBufferId, maxWorldTransforms, worldMatrixCbuffer, worldMatrix, worldMatrixArray); } void VertexShaderD3D11::reloadBytecode(const std::vector& bytecode) { ID3D11VertexShader* newObject = createVertexShader(device, bytecode, cBuffers, uniformsBufferId, maxWorldTransforms, worldMatrixCbuffer, worldMatrix, worldMatrixArray); ReleaseCheck(object); object = newObject; this->bytecode = bytecode; } ID3D11InputLayout* VertexShaderD3D11::getInputLayout11(VertexLayoutD3D11* vertexLayout) { ID3D11InputLayout* inputLayout11 = inputLayoutMap[vertexLayout]; if (!inputLayout11) { ID3D11Device* device11 = static_cast(device)->getDevice11(); HRESULT hr = device11->CreateInputLayout(vertexLayout->getElements11(), vertexLayout->getElementsCount(), bytecode.data(), bytecode.size(), &inputLayout11); RBXASSERT(SUCCEEDED(hr)); vertexLayout->registerShader(shared_from_this()); inputLayoutMap[vertexLayout] = inputLayout11; } return inputLayout11; } void VertexShaderD3D11::removeLayout(VertexLayoutD3D11* vertexLayout) { InputLayoutMap::iterator it = inputLayoutMap.find(vertexLayout); if (it != inputLayoutMap.end()) { ReleaseCheck(it->second); inputLayoutMap.erase(it); } } void VertexShaderD3D11::setWorldTransforms4x3(const float* data, size_t matrixCount) { if (worldMatrixCbuffer < 0) return; if (worldMatrix >= 0) { static const float lastRow[4] = {0,0,0,1}; float matrix[16]; memcpy(matrix, data, 3 * 4 * 4); memcpy(&matrix[12], lastRow, 16); cBuffers[worldMatrixCbuffer]->updateUniform(0, matrix, 4); } if (worldMatrixArray >= 0) cBuffers[worldMatrixCbuffer]->updateUniform(0, data, matrixCount * 3); } FragmentShaderD3D11::FragmentShaderD3D11(Device* device, const std::vector& bytecode) : FragmentShader(device) , BaseShaderD3D11(bytecode) , object(NULL) , samplerMask(0) { object = createPixelShader(device, bytecode, cBuffers, uniformsBufferId, samplerMask); } void FragmentShaderD3D11::reloadBytecode(const std::vector& bytecode) { ID3D11PixelShader* newObject = createPixelShader(device, bytecode, cBuffers, uniformsBufferId, samplerMask); ReleaseCheck(object); object = newObject; this->bytecode = bytecode; } FragmentShaderD3D11::~FragmentShaderD3D11() { ReleaseCheck(object); } VertexShaderD3D11::~VertexShaderD3D11() { ReleaseCheck(object); for (InputLayoutMap::iterator it = inputLayoutMap.begin(); it != inputLayoutMap.end(); ++it) ReleaseCheck(it->second); } static void verifyShaderSignatures(const VertexShaderD3D11* vs, const FragmentShaderD3D11* fs) { TypeD3DReflect D3DReflect = loadShaderReflector(); RBXASSERT(D3DReflect); ID3D11ShaderReflection* reflectionVS11 = NULL; ID3D11ShaderReflection* reflectionFS11 = NULL; D3DReflect(vs->getByteCode().data(), vs->getByteCode().size(), IID_ID3D11ShaderReflection, (void**) &reflectionVS11); D3DReflect(fs->getByteCode().data(), fs->getByteCode().size(), IID_ID3D11ShaderReflection, (void**) &reflectionFS11); // Get shader info D3D11_SHADER_DESC shaderDescVS; D3D11_SHADER_DESC shaderDescFS; reflectionVS11->GetDesc(&shaderDescVS); reflectionFS11->GetDesc(&shaderDescFS); RBXASSERT(shaderDescVS.OutputParameters >= shaderDescFS.InputParameters); // we have to find matching signature from FS in VS for (unsigned fsId = 0; fsId < shaderDescFS.InputParameters; ++fsId) { D3D11_SIGNATURE_PARAMETER_DESC fsDesc; reflectionFS11->GetInputParameterDesc(fsId, &fsDesc); bool found = false; for ( unsigned i=0; i< shaderDescVS.OutputParameters; i++ ) { D3D11_SIGNATURE_PARAMETER_DESC vsDesc; reflectionVS11->GetOutputParameterDesc(i, &vsDesc); if ((vsDesc.ComponentType == fsDesc.ComponentType) && (vsDesc.Register == fsDesc.Register) && (vsDesc.SemanticIndex == fsDesc.SemanticIndex) && (strcmp(vsDesc.SemanticName, fsDesc.SemanticName) == 0) && (vsDesc.SystemValueType == fsDesc.SystemValueType)) { found = true; break; } } RBXASSERT(found); } ReleaseCheck(reflectionVS11); ReleaseCheck(reflectionFS11); } ShaderProgramD3D11::ShaderProgramD3D11(Device* device, const shared_ptr& vertexShader, const shared_ptr& fragmentShader) : ShaderProgram(device, vertexShader, fragmentShader) { #ifdef __RBX_NOT_RELEASE verifyShaderSignatures(static_cast(vertexShader.get()), static_cast(fragmentShader.get())); #endif } ShaderProgramD3D11::~ShaderProgramD3D11() { static_cast(device)->getImmediateContextD3D11()->invalidateCachedProgram(); } struct IncludeCallback: ID3DInclude { boost::function fileCallback; std::map paths; IncludeCallback(boost::function fileCallback) : fileCallback(fileCallback) { } virtual HRESULT STDMETHODCALLTYPE Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) { std::string path; if (pParentData) { RBXASSERT(paths.count(pParentData)); path = paths[pParentData]; std::string::size_type slash = path.find_last_of("\\/"); path.erase(path.begin() + (slash == std::string::npos ? 0 : slash + 1), path.end()); } path += pFileName; try { std::string source = fileCallback(path); char* result = new char[source.length()]; memcpy(result, source.c_str(), source.length()); paths[result] = path; *ppData = result; *pBytes = source.length(); return S_OK; } catch (...) { return ERROR_FILE_NOT_FOUND; } } virtual HRESULT STDMETHODCALLTYPE Close(LPCVOID pData) { RBXASSERT(paths.count(pData)); paths.erase(pData); delete[] static_cast(pData); return S_OK; } }; template static std::vector consumeData(HRESULT hr, ID3DBlob* buffer, ID3DBlob* messages) { if (SUCCEEDED(hr)) { if (messages) { std::string log(static_cast(messages->GetBufferPointer()), messages->GetBufferSize()); messages->Release(); FASTLOG(FLog::Graphics, "Shader compilation resulted in warnings:"); ShaderProgram::dumpToFLog(log.c_str(), FLog::Graphics); } RBXASSERT(buffer->GetBufferSize() % sizeof(T) == 0); std::vector result(static_cast(buffer->GetBufferPointer()), static_cast(buffer->GetBufferPointer()) + buffer->GetBufferSize() / sizeof(T)); buffer->Release(); return result; } else if (messages) { std::string log(static_cast(messages->GetBufferPointer()), messages->GetBufferSize()); messages->Release(); throw std::runtime_error(log.c_str()); } else throw RBX::runtime_error("Unknown error %x", hr); } std::string ShaderProgramD3D11::createShaderSource(const std::string& path, const std::string& defines, const DeviceD3D11* device11, boost::function fileCallback) { TypeD3DPreprocess D3DPreprocess = loadShaderPreprocessor(); RBXASSERT(D3DPreprocess); // split define string into strings std::vector defineStrings; std::istringstream defineStream(defines); std::string defineTemp; while (defineStream >> defineTemp) defineStrings.push_back(defineTemp); // create d3dx defines std::vector macros; for (size_t i = 0; i < defineStrings.size(); ++i) { std::string& def = defineStrings[i]; std::string::size_type pos = def.find('='); if (pos == std::string::npos) { D3D_SHADER_MACRO macro = {def.c_str(), "1"}; macros.push_back(macro); } else { // split string into name and value def[pos] = 0; D3D_SHADER_MACRO macro = {def.c_str(), def.c_str() + pos + 1}; macros.push_back(macro); } } D3D_SHADER_MACRO macroDX11 = {"DX11", "1"}; macros.push_back(macroDX11); if (device11->getShaderProfile() == DeviceD3D11::shaderProfile_DX11_level_9_3) { D3D_SHADER_MACRO macroWinMobile= {"WIN_MOBILE", "1"}; macros.push_back(macroWinMobile); } D3D_SHADER_MACRO macroEnd = {}; macros.push_back(macroEnd); std::string sourceFolder = ""; // let preprocessor know about the original filename std::string source = "#include \"" + path + "\"\n"; IncludeCallback includeCallBack = IncludeCallback(fileCallback); ID3DBlob* text; ID3DBlob* messages; HRESULT hr = D3DPreprocess(source.c_str(), source.size(), path.c_str(), ¯os[0], &includeCallBack, &text, &messages); std::vector resultBuffer = consumeData(hr, text, messages); std::string result = &resultBuffer[0]; // preprocessor output includes a #line 1 "\memory"; remove it! if (result.size() > 10 && result.compare(0, 9, "#line 1 \"") == 0 && result[10] == ':') { std::string::size_type pos = result.find_first_of('\n'); assert(pos != std::string::npos); result.erase(result.begin(), result.begin() + pos); } return result; } static void translateShaderProfile(const std::string& originalTarget, DeviceD3D11::ShaderProfile shaderProfile, std::string& targetOut) { switch (shaderProfile) { case DeviceD3D11::shaderProfile_DX11: { std::string shaderType = originalTarget.substr(0, 2); targetOut = shaderType + "_5_0"; return; } case DeviceD3D11::shaderProfile_DX11_level_9_3: { std::string shaderType = originalTarget.substr(0, 2); targetOut = shaderType + "_4_0_level_9_3"; return; } default: break; } } bool needsBackwardCompatibility(const std::string& target) { char reqShaderProfile = target[3]; // our shaders are written in DX9 target and lower. Backwards compatibility is needed for newer targets and doesn't // hurt when shader is written in DX10 or DX11 target, then it doesn't do anything return reqShaderProfile >= '4'; } std::vector ShaderProgramD3D11::createShaderBytecode(const std::string& source, const std::string& target, const DeviceD3D11* device11, const std::string& entrypoint) { TypeD3DCompile D3DCompile = loadShaderCompiler(); RBXASSERT(D3DCompile); unsigned int flags = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; std::string realTarget; translateShaderProfile(target, device11->getShaderProfile(), realTarget); if (needsBackwardCompatibility(realTarget)) flags |= D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY; ID3DBlob* bytecode = NULL; ID3DBlob* messages = NULL; HRESULT hr = D3DCompile(source.c_str(), source.length(), entrypoint.c_str(), NULL, NULL, entrypoint.c_str(), realTarget.c_str(), flags, 0, &bytecode, &messages); return consumeData(hr, bytecode, messages); } #if !defined(RBX_PLATFORM_DURANGO) HMODULE ShaderProgramD3D11::loadShaderCompilerDLL() { static HMODULE compiler; if (!compiler) compiler = LoadLibraryA("D3DCompiler_47.dll"); return compiler; } #endif ID3D11InputLayout* ShaderProgramD3D11::getInputLayout11(VertexLayoutD3D11* vertexLayout) { return static_cast(vertexShader.get())->getInputLayout11(vertexLayout); } unsigned int ShaderProgramD3D11::getMaxWorldTransforms() const { return static_cast(vertexShader.get())->getMaxWorldTransforms(); } void ShaderProgramD3D11::setWorldTransforms4x3(const float* data, size_t matrixCount) { static_cast(vertexShader.get())->setWorldTransforms4x3(data, matrixCount); } int ShaderProgramD3D11::getConstantHandle(const char* name) const { int vs = static_cast(vertexShader.get())->findUniform(name); int fs = static_cast(fragmentShader.get())->findUniform(name); RBXASSERT(vs >= -1 && fs >= -1); if (vs < 0 && fs < 0) return -1; else return (vs + 1) | ((fs + 1) << 16); } void ShaderProgramD3D11::setConstant(int handle, const float* data, size_t vectorCount) { if (handle < 0) return; int vs = (handle & 0xffff) - 1; int fs = (handle >> 16) - 1; if (vs >= 0) { static_cast(vertexShader.get())->setConstant(vs, data, vectorCount); } if (fs >= 0) { static_cast(fragmentShader.get())->setConstant(fs, data, vectorCount); } } void ShaderProgramD3D11::uploadConstantBuffers() { static_cast(vertexShader.get())->updateConstantBuffers(); static_cast(fragmentShader.get())->updateConstantBuffers(); } unsigned int ShaderProgramD3D11::getSamplerMask() const { return static_cast(fragmentShader.get())->getSamplerMask(); } void ShaderProgramD3D11::bind() { ID3D11DeviceContext* context11 = static_cast(device)->getImmediateContext11(); VertexShaderD3D11* vs = static_cast(vertexShader.get()); FragmentShaderD3D11* fs = static_cast(fragmentShader.get()); for (auto& cb: vs->getCBuffers()) { ID3D11Buffer* buffer = cb->getObject(); context11->VSSetConstantBuffers(cb->getRegisterId(), 1, &buffer); } for (auto& cb: fs->getCBuffers()) { ID3D11Buffer* buffer = cb->getObject(); context11->PSSetConstantBuffers(cb->getRegisterId(), 1, &buffer); } context11->VSSetShader(vs->getObject(), NULL, 0); context11->PSSetShader(fs->getObject(), NULL, 0); } } }