| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #pragma once |
| 4 | |
| 5 | #include "BsPrerequisites.h" |
| 6 | #include "Material/BsMaterial.h" |
| 7 | #include "Renderer/BsRendererMaterialManager.h" |
| 8 | #include "Material/BsShaderVariation.h" |
| 9 | #include "Material/BsShader.h" |
| 10 | #include "Material/BsPass.h" |
| 11 | #include "RenderAPI/BsRenderAPI.h" |
| 12 | |
| 13 | #if BS_PROFILING_ENABLED |
| 14 | #include "Profiling/BsProfilerGPU.h" |
| 15 | #endif |
| 16 | |
| 17 | /** @addtogroup Renderer-Engine-Internal |
| 18 | * @{ |
| 19 | */ |
| 20 | |
| 21 | /** References the shader path in RendererMaterial implementation. */ |
| 22 | #define RMAT_DEF(path) \ |
| 23 | public: \ |
| 24 | static void _initMetaData() \ |
| 25 | { \ |
| 26 | bs::RendererMaterialManager::_registerMaterial(&mMetaData, path); \ |
| 27 | }; \ |
| 28 | |
| 29 | /** |
| 30 | * References the shader path in RendererMaterial implementation. Provides an _initDefines() method allowing the C++ |
| 31 | * code to provide preprocessor defines to be set when compiling the shader. Note that when changing these defines you need |
| 32 | * to manually force the shader to be reimported. |
| 33 | */ |
| 34 | #define RMAT_DEF_CUSTOMIZED(path) \ |
| 35 | public: \ |
| 36 | static void _initMetaData() \ |
| 37 | { \ |
| 38 | _initDefines(mMetaData.defines); \ |
| 39 | bs::RendererMaterialManager::_registerMaterial(&mMetaData, path); \ |
| 40 | }; \ |
| 41 | static void _initDefines(ShaderDefines& defines); |
| 42 | |
| 43 | /** @} */ |
| 44 | |
| 45 | namespace bs { namespace ct |
| 46 | { |
| 47 | /** @addtogroup Renderer-Engine-Internal |
| 48 | * @{ |
| 49 | */ |
| 50 | |
| 51 | /** Contains data common to all render material instances of a specific type. */ |
| 52 | struct RendererMaterialMetaData |
| 53 | { |
| 54 | Path shaderPath; |
| 55 | SPtr<Shader> shader; |
| 56 | SPtr<Shader> overrideShader; |
| 57 | SmallVector<RendererMaterialBase*, 4> instances; |
| 58 | ShaderVariations variations; |
| 59 | ShaderDefines defines; |
| 60 | |
| 61 | #if BS_PROFILING_ENABLED |
| 62 | ProfilerString profilerSampleName; |
| 63 | #endif |
| 64 | }; |
| 65 | |
| 66 | /** |
| 67 | * Helper class that performs GPU profiling in the current block. Profiling sample is started when the class is |
| 68 | * constructed and ended upon destruction. |
| 69 | */ |
| 70 | struct RendererMaterialProfileBlock : ProfileGPUBlock |
| 71 | { |
| 72 | RendererMaterialProfileBlock(const RendererMaterialMetaData& metaData) |
| 73 | :ProfileGPUBlock(metaData.profilerSampleName) |
| 74 | { } |
| 75 | }; |
| 76 | |
| 77 | #define BS_RENMAT_PROFILE_BLOCK RendererMaterialProfileBlock __sampleBlock(mMetaData); |
| 78 | |
| 79 | /** Base class for all RendererMaterial instances, containing common data and methods. */ |
| 80 | class BS_EXPORT RendererMaterialBase |
| 81 | { |
| 82 | public: |
| 83 | virtual ~RendererMaterialBase() = default; |
| 84 | |
| 85 | /** Returns the shader used by the material. */ |
| 86 | SPtr<Shader> getShader() const { return mShader; } |
| 87 | |
| 88 | /** Returns the internal parameter set containing GPU bindable parameters. */ |
| 89 | SPtr<GpuParams> getParams() const { return mParams; } |
| 90 | |
| 91 | /** |
| 92 | * Helper field to be set before construction. Identifiers the variation of the material to initialize this |
| 93 | * object with. |
| 94 | */ |
| 95 | UINT32 _varIdx; |
| 96 | protected: |
| 97 | friend class RendererMaterialManager; |
| 98 | |
| 99 | SPtr<GpuParams> mParams; |
| 100 | SPtr<GraphicsPipelineState> mGfxPipeline; |
| 101 | SPtr<ComputePipelineState> mComputePipeline; |
| 102 | UINT32 mStencilRef = 0; |
| 103 | |
| 104 | ShaderVariation mVariation; |
| 105 | SPtr<Shader> mShader; |
| 106 | }; |
| 107 | |
| 108 | /** Helper class to initialize all renderer materials as soon as the library is loaded. */ |
| 109 | template <class T> |
| 110 | struct InitRendererMaterialStart |
| 111 | { |
| 112 | public: |
| 113 | InitRendererMaterialStart() |
| 114 | { |
| 115 | T::_initMetaData(); |
| 116 | } |
| 117 | |
| 118 | /** Forces the compiler to not optimize out construction of this type. */ |
| 119 | void instantiate() { } |
| 120 | }; |
| 121 | |
| 122 | /** Wrapper class around Material that allows a simple way to load and set up materials used by the renderer. */ |
| 123 | template<class T> |
| 124 | class RendererMaterial : public RendererMaterialBase |
| 125 | { |
| 126 | public: |
| 127 | virtual ~RendererMaterial() = default; |
| 128 | |
| 129 | /** |
| 130 | * Retrieves an instance of this renderer material. If material has multiple variations the first available |
| 131 | * variation will be returned. |
| 132 | */ |
| 133 | static T* get() |
| 134 | { |
| 135 | if(mMetaData.instances[0] == nullptr) |
| 136 | { |
| 137 | RendererMaterialBase* mat = bs_alloc<T>(); |
| 138 | mat->_varIdx = 0; |
| 139 | new (mat) T(); |
| 140 | |
| 141 | mMetaData.instances[0] = mat; |
| 142 | } |
| 143 | |
| 144 | return (T*)mMetaData.instances[0]; |
| 145 | } |
| 146 | |
| 147 | /** Retrieves an instance of a particular variation of this renderer material. */ |
| 148 | static T* get(const ShaderVariation& variation) |
| 149 | { |
| 150 | if(variation.getIdx() == (UINT32)-1) |
| 151 | variation.setIdx(mMetaData.variations.find(variation)); |
| 152 | |
| 153 | UINT32 varIdx = variation.getIdx(); |
| 154 | if(mMetaData.instances[varIdx] == nullptr) |
| 155 | { |
| 156 | RendererMaterialBase* mat = bs_alloc<T>(); |
| 157 | mat->_varIdx = varIdx; |
| 158 | new (mat) T(); |
| 159 | |
| 160 | mMetaData.instances[varIdx] = mat; |
| 161 | } |
| 162 | |
| 163 | return (T*)mMetaData.instances[varIdx]; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Sets a shader that is to be used instead of the default shader for this material. Set to null to revert back |
| 168 | * to using the default shader. All existing instances of the material will be invalidated (get() methods need to |
| 169 | * be called again). |
| 170 | */ |
| 171 | static void setOverride(const SPtr<Shader>& shader) |
| 172 | { |
| 173 | if(mMetaData.overrideShader == shader) |
| 174 | return; |
| 175 | |
| 176 | for(UINT32 i = 0; i < mMetaData.instances.size(); i++) |
| 177 | { |
| 178 | if (mMetaData.instances[i] != nullptr) |
| 179 | bs_delete(mMetaData.instances[i]); |
| 180 | |
| 181 | mMetaData.instances[i] = nullptr; |
| 182 | } |
| 183 | |
| 184 | mMetaData.overrideShader = shader; |
| 185 | } |
| 186 | |
| 187 | /** Returns the path to the built-in (non-overriden) shader used by this material. */ |
| 188 | static Path getShaderPath() { return mMetaData.shaderPath; } |
| 189 | |
| 190 | /** Returns a set of dynamically defined defines used when compiling this shader. */ |
| 191 | static ShaderDefines getShaderDefines() { return mMetaData.defines; } |
| 192 | |
| 193 | /** |
| 194 | * Binds the materials and its parameters to the pipeline. This material will be used for rendering any subsequent |
| 195 | * draw calls, or executing dispatch calls. If @p bindParams is false you need to call bindParams() separately |
| 196 | * to bind material parameters (if any). |
| 197 | */ |
| 198 | void bind(bool bindParams = true) const |
| 199 | { |
| 200 | RenderAPI& rapi = RenderAPI::instance(); |
| 201 | |
| 202 | if(mGfxPipeline) |
| 203 | { |
| 204 | rapi.setGraphicsPipeline(mGfxPipeline); |
| 205 | rapi.setStencilRef(mStencilRef); |
| 206 | } |
| 207 | else |
| 208 | rapi.setComputePipeline(mComputePipeline); |
| 209 | |
| 210 | if(bindParams) |
| 211 | rapi.setGpuParams(mParams); |
| 212 | } |
| 213 | |
| 214 | /** Binds the material parameters to the pipeline. */ |
| 215 | void bindParams() const |
| 216 | { |
| 217 | RenderAPI& rapi = RenderAPI::instance(); |
| 218 | rapi.setGpuParams(mParams); |
| 219 | } |
| 220 | |
| 221 | protected: |
| 222 | RendererMaterial() |
| 223 | { |
| 224 | mInitOnStart.instantiate(); |
| 225 | |
| 226 | if(mMetaData.overrideShader) |
| 227 | mShader = mMetaData.overrideShader; |
| 228 | else |
| 229 | mShader = mMetaData.shader; |
| 230 | |
| 231 | mVariation = mMetaData.variations.get(_varIdx); |
| 232 | |
| 233 | const Vector<SPtr<Technique>>& techniques = mShader->getTechniques(); |
| 234 | for(auto& entry : techniques) |
| 235 | { |
| 236 | if(!entry->isSupported()) |
| 237 | continue; |
| 238 | |
| 239 | if(entry->getVariation() == mVariation) |
| 240 | { |
| 241 | SPtr<Pass> pass = entry->getPass(0); |
| 242 | pass->compile(); |
| 243 | |
| 244 | mGfxPipeline = pass->getGraphicsPipelineState(); |
| 245 | if (mGfxPipeline != nullptr) |
| 246 | mParams = GpuParams::create(mGfxPipeline); |
| 247 | else |
| 248 | { |
| 249 | mComputePipeline = pass->getComputePipelineState(); |
| 250 | mParams = GpuParams::create(mComputePipeline); |
| 251 | } |
| 252 | |
| 253 | // Assign default values from the shader |
| 254 | const auto& textureParams = mShader->getTextureParams(); |
| 255 | for(auto& param : textureParams) |
| 256 | { |
| 257 | UINT32 defaultValueIdx = param.second.defaultValueIdx; |
| 258 | if(defaultValueIdx == (UINT32)-1) |
| 259 | continue; |
| 260 | |
| 261 | for (UINT32 i = 0; i < 6; i++) |
| 262 | { |
| 263 | GpuProgramType progType = (GpuProgramType)i; |
| 264 | |
| 265 | for(auto& varName : param.second.gpuVariableNames) |
| 266 | { |
| 267 | if(mParams->hasTexture(progType, varName)) |
| 268 | { |
| 269 | SPtr<Texture> texture = mShader->getDefaultTexture(defaultValueIdx); |
| 270 | mParams->setTexture(progType, varName, texture); |
| 271 | } |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | const auto& samplerParams = mShader->getSamplerParams(); |
| 277 | for(auto& param : samplerParams) |
| 278 | { |
| 279 | UINT32 defaultValueIdx = param.second.defaultValueIdx; |
| 280 | if(defaultValueIdx == (UINT32)-1) |
| 281 | continue; |
| 282 | |
| 283 | for (UINT32 i = 0; i < 6; i++) |
| 284 | { |
| 285 | GpuProgramType progType = (GpuProgramType)i; |
| 286 | |
| 287 | for(auto& varName : param.second.gpuVariableNames) |
| 288 | { |
| 289 | if(mParams->hasSamplerState(progType, varName)) |
| 290 | { |
| 291 | SPtr<SamplerState> samplerState = mShader->getDefaultSampler(defaultValueIdx); |
| 292 | mParams->setSamplerState(progType, varName, samplerState); |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | mStencilRef = pass->getStencilRefValue(); |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | friend class RendererMaterialManager; |
| 304 | |
| 305 | static RendererMaterialMetaData mMetaData; |
| 306 | static InitRendererMaterialStart<T> mInitOnStart; |
| 307 | }; |
| 308 | |
| 309 | template<class T> |
| 310 | InitRendererMaterialStart<T> RendererMaterial<T>::mInitOnStart; |
| 311 | |
| 312 | template<class T> |
| 313 | RendererMaterialMetaData RendererMaterial<T>::mMetaData; |
| 314 | |
| 315 | /** @} */ |
| 316 | }} |