#include "BsShader.h" #include "BsTechnique.h" #include "BsException.h" #include "BsDebug.h" #include "BsShaderRTTI.h" #include "BsResources.h" #include "BsGpuParams.h" #include "BsFrameAlloc.h" #include "BsPass.h" #include "BsSamplerState.h" namespace BansheeEngine { std::atomic ShaderCore::mNextShaderId = 0; template TSHADER_DESC::TSHADER_DESC() :queuePriority(0), queueSortType(QueueSortType::None), separablePasses(false), flags(0) { } template void TSHADER_DESC::addParameter(const String& name, const String& gpuVariableName, GpuParamDataType type, StringID rendererSemantic, UINT32 arraySize, UINT32 elementSize, UINT8* defaultValue) { if(type == GPDT_STRUCT && elementSize <= 0) BS_EXCEPT(InvalidParametersException, "You need to provide a non-zero element size for a struct parameter.") SHADER_DATA_PARAM_DESC desc; desc.name = name; desc.gpuVariableName = gpuVariableName; desc.type = type; desc.arraySize = arraySize; desc.rendererSemantic = rendererSemantic; desc.elementSize = elementSize; dataParams[name] = desc; if (defaultValue != nullptr) { desc.defaultValueIdx = (UINT32)dataDefaultValues.size(); UINT32 defaultValueSize = Shader::getDataParamSize(type); dataDefaultValues.resize(desc.defaultValueIdx + defaultValueSize); memcpy(&dataDefaultValues[desc.defaultValueIdx], defaultValue, defaultValueSize); } else desc.defaultValueIdx = (UINT32)-1; } template void TSHADER_DESC::addParameter(const String& name, const String& gpuVariableName, GpuParamObjectType type, StringID rendererSemantic) { UINT32 defaultValueIdx = (UINT32)-1; addParameterInternal(name, gpuVariableName, type, rendererSemantic, defaultValueIdx); } template void TSHADER_DESC::addParameter(const String& name, const String& gpuVariableName, GpuParamObjectType type, const SamplerStateType& defaultValue, StringID rendererSemantic) { UINT32 defaultValueIdx = (UINT32)-1; if (Shader::isSampler(type) && defaultValue != nullptr) { defaultValueIdx = (UINT32)samplerDefaultValues.size(); samplerDefaultValues.push_back(defaultValue); } addParameterInternal(name, gpuVariableName, type, rendererSemantic, defaultValueIdx); } template void TSHADER_DESC::addParameter(const String& name, const String& gpuVariableName, GpuParamObjectType type, const TextureType& defaultValue, StringID rendererSemantic) { UINT32 defaultValueIdx = (UINT32)-1; if (Shader::isTexture(type) && defaultValue != nullptr) { defaultValueIdx = (UINT32)textureDefaultValues.size(); textureDefaultValues.push_back(defaultValue); } addParameterInternal(name, gpuVariableName, type, rendererSemantic, defaultValueIdx); } template void TSHADER_DESC::addParameterInternal(const String& name, const String& gpuVariableName, GpuParamObjectType type, StringID rendererSemantic, UINT32 defaultValueIdx) { Map* DEST_LOOKUP[] = { &textureParams, &bufferParams, &samplerParams }; UINT32 destIdx = 0; if (Shader::isBuffer(type)) destIdx = 1; else if (Shader::isSampler(type)) destIdx = 2; Map& paramsMap = *DEST_LOOKUP[destIdx]; auto iterFind = paramsMap.find(name); if (iterFind == paramsMap.end()) { SHADER_OBJECT_PARAM_DESC desc; desc.name = name; desc.type = type; desc.rendererSemantic = rendererSemantic; desc.gpuVariableNames.push_back(gpuVariableName); desc.defaultValueIdx = defaultValueIdx; paramsMap[name] = desc; } else { SHADER_OBJECT_PARAM_DESC& desc = iterFind->second; // If same name but different properties, we ignore this param if (desc.type != type || desc.rendererSemantic != rendererSemantic) return; Vector& gpuVariableNames = desc.gpuVariableNames; bool found = false; for (UINT32 i = 0; i < (UINT32)gpuVariableNames.size(); i++) { if (gpuVariableNames[i] == gpuVariableName) { found = true; break; } } if (!found) gpuVariableNames.push_back(gpuVariableName); } } template void TSHADER_DESC::setParamBlockAttribs(const String& name, bool shared, GpuParamBlockUsage usage, StringID rendererSemantic) { SHADER_PARAM_BLOCK_DESC desc; desc.name = name; desc.shared = shared; desc.usage = usage; desc.rendererSemantic = rendererSemantic; paramBlocks[name] = desc; } template struct TSHADER_DESC; template struct TSHADER_DESC; template TShader::TShader(const String& name, const TSHADER_DESC& desc, const Vector>& techniques, UINT32 id) :mName(name), mDesc(desc), mTechniques(techniques), mId(id) { } template TShader::~TShader() { } template GpuParamType TShader::getParamType(const String& name) const { auto findIterData = mDesc.dataParams.find(name); if (findIterData != mDesc.dataParams.end()) return GPT_DATA; auto findIterTexture = mDesc.textureParams.find(name); if (findIterTexture != mDesc.textureParams.end()) return GPT_TEXTURE; auto findIterBuffer = mDesc.bufferParams.find(name); if (findIterBuffer != mDesc.bufferParams.end()) return GPT_BUFFER; auto findIterSampler = mDesc.samplerParams.find(name); if (findIterSampler != mDesc.samplerParams.end()) return GPT_SAMPLER; BS_EXCEPT(InternalErrorException, "Cannot find the parameter with the name: " + name); } template const SHADER_DATA_PARAM_DESC& TShader::getDataParamDesc(const String& name) const { auto findIterData = mDesc.dataParams.find(name); if (findIterData != mDesc.dataParams.end()) return findIterData->second; BS_EXCEPT(InternalErrorException, "Cannot find the parameter with the name: " + name); } template const SHADER_OBJECT_PARAM_DESC& TShader::getTextureParamDesc(const String& name) const { auto findIterObject = mDesc.textureParams.find(name); if (findIterObject != mDesc.textureParams.end()) return findIterObject->second; BS_EXCEPT(InternalErrorException, "Cannot find the parameter with the name: " + name); } template const SHADER_OBJECT_PARAM_DESC& TShader::getSamplerParamDesc(const String& name) const { auto findIterObject = mDesc.samplerParams.find(name); if (findIterObject != mDesc.samplerParams.end()) return findIterObject->second; BS_EXCEPT(InternalErrorException, "Cannot find the parameter with the name: " + name); } template const SHADER_OBJECT_PARAM_DESC& TShader::getBufferParamDesc(const String& name) const { auto findIterObject = mDesc.bufferParams.find(name); if (findIterObject != mDesc.bufferParams.end()) return findIterObject->second; BS_EXCEPT(InternalErrorException, "Cannot find the parameter with the name: " + name); } template bool TShader::hasDataParam(const String& name) const { auto findIterData = mDesc.dataParams.find(name); if (findIterData != mDesc.dataParams.end()) return true; return false; } template bool TShader::hasTextureParam(const String& name) const { auto findIterObject = mDesc.textureParams.find(name); if (findIterObject != mDesc.textureParams.end()) return true; return false; } template bool TShader::hasSamplerParam(const String& name) const { auto findIterObject = mDesc.samplerParams.find(name); if (findIterObject != mDesc.samplerParams.end()) return true; return false; } template bool TShader::hasBufferParam(const String& name) const { auto findIterObject = mDesc.bufferParams.find(name); if (findIterObject != mDesc.bufferParams.end()) return true; return false; } template typename TShader::TextureType TShader::getDefaultTexture(UINT32 index) const { if (index < (UINT32)mDesc.textureDefaultValues.size()) return mDesc.textureDefaultValues[index]; return TextureType(); } template typename TShader::SamplerStateType TShader::getDefaultSampler(UINT32 index) const { if (index < (UINT32)mDesc.samplerDefaultValues.size()) return mDesc.samplerDefaultValues[index]; return SamplerStateType(); } template UINT8* TShader::getDefaultValue(UINT32 index) const { if (index < (UINT32)mDesc.dataDefaultValues.size()) return (UINT8*)&mDesc.dataDefaultValues[index]; return nullptr; } template SPtr::TechniqueType> TShader::getBestTechnique() const { for (auto iter = mTechniques.begin(); iter != mTechniques.end(); ++iter) { if ((*iter)->isSupported()) { return *iter; } } return nullptr; // TODO - Low priority. Instead of returning null use an extremely simple technique that will be supported almost everywhere as a fallback. } template class TShader < false > ; template class TShader < true >; ShaderCore::ShaderCore(const String& name, const SHADER_DESC_CORE& desc, const Vector>& techniques, UINT32 id) :TShader(name, desc, techniques, id) { } SPtr ShaderCore::create(const String& name, const SHADER_DESC_CORE& desc, const Vector>& techniques) { UINT32 id = mNextShaderId.fetch_add(1, std::memory_order_relaxed); assert(id < std::numeric_limits::max() && "Created too many shaders, reached maximum id."); ShaderCore* shaderCore = new (bs_alloc()) ShaderCore(name, desc, techniques, id); SPtr shaderCorePtr = bs_shared_ptr(shaderCore); shaderCorePtr->_setThisPtr(shaderCorePtr); shaderCorePtr->initialize(); return shaderCorePtr; } Shader::Shader(const String& name, const SHADER_DESC& desc, const Vector>& techniques, UINT32 id) :TShader(name, desc, techniques, id) { mMetaData = bs_shared_ptr_new(); } SPtr Shader::getCore() const { return std::static_pointer_cast(mCoreSpecific); } void Shader::setIncludeFiles(const Vector& includes) { SPtr meta = std::static_pointer_cast(getMetaData()); meta->includes = includes; } SPtr Shader::createCore() const { Vector> techniques; for (auto& technique : mTechniques) techniques.push_back(technique->getCore()); ShaderCore* shaderCore = new (bs_alloc()) ShaderCore(mName, convertDesc(mDesc), techniques, mId); SPtr shaderCorePtr = bs_shared_ptr(shaderCore); shaderCorePtr->_setThisPtr(shaderCorePtr); return shaderCorePtr; } SHADER_DESC_CORE Shader::convertDesc(const SHADER_DESC& desc) const { SHADER_DESC_CORE output; output.dataParams = desc.dataParams; output.textureParams = desc.textureParams; output.samplerParams = desc.samplerParams; output.bufferParams = desc.bufferParams; output.paramBlocks = desc.paramBlocks; output.queuePriority = desc.queuePriority; output.queueSortType = desc.queueSortType; output.separablePasses = desc.separablePasses; // Ignoring default values as they are not needed for syncing since // they're initialized through the material. return output; } void Shader::getCoreDependencies(Vector& dependencies) { for (auto& technique : mTechniques) dependencies.push_back(technique.get()); } bool Shader::isSampler(GpuParamObjectType type) { switch(type) { case GPOT_SAMPLER1D: case GPOT_SAMPLER2D: case GPOT_SAMPLER3D: case GPOT_SAMPLERCUBE: case GPOT_SAMPLER2DMS: return true; } return false; } bool Shader::isTexture(GpuParamObjectType type) { switch(type) { case GPOT_TEXTURE1D: case GPOT_TEXTURE2D: case GPOT_TEXTURE3D: case GPOT_TEXTURECUBE: case GPOT_TEXTURE2DMS: return true; } return false; } bool Shader::isBuffer(GpuParamObjectType type) { switch(type) { case GPOT_BYTE_BUFFER: case GPOT_STRUCTURED_BUFFER: case GPOT_RWBYTE_BUFFER: case GPOT_RWAPPEND_BUFFER: case GPOT_RWCONSUME_BUFFER: case GPOT_RWSTRUCTURED_BUFFER: case GPOT_RWSTRUCTURED_BUFFER_WITH_COUNTER: case GPOT_RWTYPED_BUFFER: return true; } return false; } UINT32 Shader::getDataParamSize(GpuParamDataType type) { static const GpuDataParamInfos PARAM_SIZES; UINT32 idx = (UINT32)type; if (idx < sizeof(GpuParams::PARAM_SIZES.lookup)) return GpuParams::PARAM_SIZES.lookup[idx].size; return 0; } HShader Shader::create(const String& name, const SHADER_DESC& desc, const Vector>& techniques) { ShaderPtr newShader = _createPtr(name, desc, techniques); return static_resource_cast(gResources()._createResourceHandle(newShader)); } ShaderPtr Shader::_createPtr(const String& name, const SHADER_DESC& desc, const Vector>& techniques) { UINT32 id = ShaderCore::mNextShaderId.fetch_add(1, std::memory_order_relaxed); assert(id < std::numeric_limits::max() && "Created too many shaders, reached maximum id."); ShaderPtr newShader = bs_core_ptr(new (bs_alloc()) Shader(name, desc, techniques, id)); newShader->_setThisPtr(newShader); newShader->initialize(); return newShader; } ShaderPtr Shader::createEmpty() { ShaderPtr newShader = bs_core_ptr(new (bs_alloc()) Shader()); newShader->_setThisPtr(newShader); return newShader; } RTTITypeBase* Shader::getRTTIStatic() { return ShaderRTTI::instance(); } RTTITypeBase* Shader::getRTTI() const { return Shader::getRTTIStatic(); } RTTITypeBase* ShaderMetaData::getRTTIStatic() { return ShaderMetaDataRTTI::instance(); } RTTITypeBase* ShaderMetaData::getRTTI() const { return ShaderMetaData::getRTTIStatic(); } }