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