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 | }} |