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 "BsCorePrerequisites.h" |
6 | #include "RenderAPI/BsGpuParamDesc.h" |
7 | #include "RenderAPI/BsGpuParams.h" |
8 | #include "RenderAPI/BsRenderAPI.h" |
9 | #include "RenderAPI/BsGpuParamBlockBuffer.h" |
10 | |
11 | namespace bs { namespace ct |
12 | { |
13 | /** @addtogroup Renderer-Internal |
14 | * @{ |
15 | */ |
16 | |
17 | /** Wrapper for a single parameter in a parameter block buffer. */ |
18 | template<class T> |
19 | class ParamBlockParam |
20 | { |
21 | public: |
22 | ParamBlockParam() = default; |
23 | ParamBlockParam(const GpuParamDataDesc& paramDesc) |
24 | :mParamDesc(paramDesc) |
25 | { } |
26 | |
27 | /** |
28 | * Sets the parameter in the provided parameter block buffer. Caller is responsible for ensuring the param block |
29 | * buffer contains this parameter. |
30 | */ |
31 | void set(const SPtr<GpuParamBlockBuffer>& paramBlock, const T& value, UINT32 arrayIdx = 0) const |
32 | { |
33 | #if BS_DEBUG_MODE |
34 | if (arrayIdx >= mParamDesc.arraySize) |
35 | { |
36 | BS_EXCEPT(InvalidParametersException, "Array index out of range. Array size: " + |
37 | toString(mParamDesc.arraySize) + ". Requested size: " + toString(arrayIdx)); |
38 | } |
39 | #endif |
40 | |
41 | UINT32 elementSizeBytes = mParamDesc.elementSize * sizeof(UINT32); |
42 | UINT32 sizeBytes = std::min(elementSizeBytes, (UINT32)sizeof(T)); // Truncate if it doesn't fit within parameter size |
43 | |
44 | const bool transposeMatrices = gCaps().conventions.matrixOrder == Conventions::MatrixOrder::ColumnMajor; |
45 | if (TransposePolicy<T>::transposeEnabled(transposeMatrices)) |
46 | { |
47 | auto transposed = TransposePolicy<T>::transpose(value); |
48 | paramBlock->write((mParamDesc.cpuMemOffset + arrayIdx * mParamDesc.arrayElementStride) * sizeof(UINT32), |
49 | &transposed, sizeBytes); |
50 | } |
51 | else |
52 | paramBlock->write((mParamDesc.cpuMemOffset + arrayIdx * mParamDesc.arrayElementStride) * sizeof(UINT32), |
53 | &value, sizeBytes); |
54 | |
55 | // Set unused bytes to 0 |
56 | if (sizeBytes < elementSizeBytes) |
57 | { |
58 | UINT32 diffSize = elementSizeBytes - sizeBytes; |
59 | paramBlock->zeroOut((mParamDesc.cpuMemOffset + arrayIdx * mParamDesc.arrayElementStride) * sizeof(UINT32) + |
60 | sizeBytes, diffSize); |
61 | } |
62 | } |
63 | |
64 | /** |
65 | * Gets the parameter in the provided parameter block buffer. Caller is responsible for ensuring the param block |
66 | * buffer contains this parameter. |
67 | */ |
68 | T get(const SPtr<GpuParamBlockBuffer>& paramBlock, UINT32 arrayIdx = 0) const |
69 | { |
70 | #if BS_DEBUG_MODE |
71 | if (arrayIdx >= mParamDesc.arraySize) |
72 | { |
73 | LOGERR("Array index out of range. Array size: " + toString(mParamDesc.arraySize) + ". Requested size: " + |
74 | toString(arrayIdx)); |
75 | return T(); |
76 | } |
77 | #endif |
78 | |
79 | UINT32 elementSizeBytes = mParamDesc.elementSize * sizeof(UINT32); |
80 | UINT32 sizeBytes = std::min(elementSizeBytes, (UINT32)sizeof(T)); |
81 | |
82 | T value; |
83 | paramBlock->read((mParamDesc.cpuMemOffset + arrayIdx * mParamDesc.arrayElementStride) * sizeof(UINT32), &value, |
84 | sizeBytes); |
85 | |
86 | return value; |
87 | } |
88 | |
89 | protected: |
90 | GpuParamDataDesc mParamDesc; |
91 | }; |
92 | |
93 | /** Base class for all parameter blocks. */ |
94 | struct BS_CORE_EXPORT ParamBlock |
95 | { |
96 | virtual ~ParamBlock(); |
97 | virtual void initialize() = 0; |
98 | }; |
99 | |
100 | /** |
101 | * Takes care of initializing param block definitions in a delayed manner since they depend on engine systems yet |
102 | * are usually used as global variables which are initialized before engine systems are ready. |
103 | */ |
104 | class BS_CORE_EXPORT ParamBlockManager : public Module<ParamBlockManager> |
105 | { |
106 | public: |
107 | ParamBlockManager(); |
108 | |
109 | /** Registers a new param block, and initializes it when ready. */ |
110 | static void registerBlock(ParamBlock* paramBlock); |
111 | |
112 | /** Removes the param block from the initialization list. */ |
113 | static void unregisterBlock(ParamBlock* paramBlock); |
114 | |
115 | private: |
116 | static Vector<ParamBlock*> sToInitialize; |
117 | }; |
118 | |
119 | /** |
120 | * Starts a new custom parameter block. Custom parameter blocks allow you to create C++ structures that map directly |
121 | * to GPU program buffers (for example uniform buffer in OpenGL or constant buffer in DX). Must be followed by |
122 | * BS_PARAM_BLOCK_END. |
123 | */ |
124 | #define BS_PARAM_BLOCK_BEGIN(Name) \ |
125 | struct Name : ParamBlock \ |
126 | { \ |
127 | Name() \ |
128 | { \ |
129 | ParamBlockManager::registerBlock(this); \ |
130 | } \ |
131 | \ |
132 | SPtr<GpuParamBlockBuffer> createBuffer() const { return GpuParamBlockBuffer::create(mBlockSize); } \ |
133 | \ |
134 | private: \ |
135 | friend class ParamBlockManager; \ |
136 | \ |
137 | void initialize() override \ |
138 | { \ |
139 | mParams = getEntries(); \ |
140 | RenderAPI& rapi = RenderAPI::instance(); \ |
141 | \ |
142 | GpuParamBlockDesc blockDesc = rapi.generateParamBlockDesc(#Name, mParams); \ |
143 | mBlockSize = blockDesc.blockSize * sizeof(UINT32); \ |
144 | \ |
145 | initEntries(); \ |
146 | } \ |
147 | \ |
148 | struct META_FirstEntry {}; \ |
149 | static void META_GetPrevEntries(Vector<GpuParamDataDesc>& params, META_FirstEntry id) { } \ |
150 | void META_InitPrevEntry(const Vector<GpuParamDataDesc>& params, UINT32 idx, META_FirstEntry id) { } \ |
151 | \ |
152 | typedef META_FirstEntry |
153 | |
154 | /** |
155 | * Registers a new entry in a parameter block. Must be called in between BS_PARAM_BLOCK_BEGIN and BS_PARAM_BLOCK_END calls. |
156 | */ |
157 | #define BS_PARAM_BLOCK_ENTRY_ARRAY(Type, Name, NumElements) \ |
158 | META_Entry_##Name; \ |
159 | \ |
160 | struct META_NextEntry_##Name {}; \ |
161 | static void META_GetPrevEntries(Vector<GpuParamDataDesc>& params, META_NextEntry_##Name id) \ |
162 | { \ |
163 | META_GetPrevEntries(params, META_Entry_##Name()); \ |
164 | \ |
165 | params.push_back(GpuParamDataDesc()); \ |
166 | GpuParamDataDesc& newEntry = params.back(); \ |
167 | newEntry.name = #Name; \ |
168 | newEntry.type = (GpuParamDataType)TGpuDataParamInfo<Type>::TypeId; \ |
169 | newEntry.arraySize = NumElements; \ |
170 | newEntry.elementSize = sizeof(Type); \ |
171 | } \ |
172 | \ |
173 | void META_InitPrevEntry(const Vector<GpuParamDataDesc>& params, UINT32 idx, META_NextEntry_##Name id) \ |
174 | { \ |
175 | META_InitPrevEntry(params, idx - 1, META_Entry_##Name()); \ |
176 | Name = ParamBlockParam<Type>(params[idx]); \ |
177 | } \ |
178 | \ |
179 | public: \ |
180 | ParamBlockParam<Type> Name; \ |
181 | \ |
182 | private: \ |
183 | typedef META_NextEntry_##Name |
184 | |
185 | /** |
186 | * Registers a new entry in a parameter block. Must be called in between BS_PARAM_BLOCK_BEGIN and BS_PARAM_BLOCK_END calls. |
187 | */ |
188 | #define BS_PARAM_BLOCK_ENTRY(Type, Name) BS_PARAM_BLOCK_ENTRY_ARRAY(Type, Name, 1) |
189 | |
190 | /** Ends parameter block definition. See BS_PARAM_BLOCK_BEGIN. */ |
191 | #define BS_PARAM_BLOCK_END \ |
192 | META_LastEntry; \ |
193 | \ |
194 | static Vector<GpuParamDataDesc> getEntries() \ |
195 | { \ |
196 | Vector<GpuParamDataDesc> entries; \ |
197 | META_GetPrevEntries(entries, META_LastEntry()); \ |
198 | return entries; \ |
199 | } \ |
200 | \ |
201 | void initEntries() \ |
202 | { \ |
203 | META_InitPrevEntry(mParams, (UINT32)mParams.size() - 1, META_LastEntry()); \ |
204 | } \ |
205 | \ |
206 | Vector<GpuParamDataDesc> mParams; \ |
207 | UINT32 mBlockSize; \ |
208 | }; |
209 | |
210 | /** @} */ |
211 | }} |