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