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#include "Material/BsGpuParamsSet.h"
4#include "Material/BsShader.h"
5#include "Material/BsTechnique.h"
6#include "Material/BsPass.h"
7#include "RenderAPI/BsGpuProgram.h"
8#include "RenderAPI/BsGpuPipelineState.h"
9#include "Material/BsMaterialParams.h"
10#include "RenderAPI/BsGpuParamDesc.h"
11#include "RenderAPI/BsRenderAPI.h"
12#include "RenderAPI/BsGpuParamBlockBuffer.h"
13#include "Animation/BsAnimationCurve.h"
14#include "Image/BsColorGradient.h"
15#include "Image/BsSpriteTexture.h"
16
17namespace bs
18{
19 /** Uniquely identifies a GPU parameter. */
20 struct ValidParamKey
21 {
22 ValidParamKey(const String& name, const MaterialParams::ParamType& type)
23 :name(name), type(type)
24 { }
25
26 bool operator== (const ValidParamKey& rhs) const
27 {
28 return name == rhs.name && type == rhs.type;
29 }
30
31 bool operator!= (const ValidParamKey& rhs) const
32 {
33 return !(*this == rhs);
34 }
35
36 String name;
37 MaterialParams::ParamType type;
38 };
39}
40
41/** @cond STDLIB */
42
43namespace std
44{
45 /** Hash value generator for ValidParamKey. */
46 template<>
47 struct hash<bs::ValidParamKey>
48 {
49 size_t operator()(const bs::ValidParamKey& key) const
50 {
51 size_t hash = 0;
52 bs::bs_hash_combine(hash, key.name);
53 bs::bs_hash_combine(hash, key.type);
54
55 return hash;
56 }
57 };
58}
59
60/** @endcond */
61
62namespace bs
63{
64 struct ShaderBlockDesc
65 {
66 String name;
67 GpuBufferUsage usage;
68 int size;
69 bool external;
70 UINT32 sequentialIdx;
71 UINT32 set;
72 UINT32 slot;
73 };
74
75 Vector<SPtr<GpuParamDesc>> getAllParamDescs(const SPtr<Technique>& technique)
76 {
77 Vector<SPtr<GpuParamDesc>> allParamDescs;
78
79 // Make sure all gpu programs are fully loaded
80 for (UINT32 i = 0; i < technique->getNumPasses(); i++)
81 {
82 SPtr<Pass> curPass = technique->getPass(i);
83
84 const SPtr<GraphicsPipelineState>& graphicsPipeline = curPass->getGraphicsPipelineState();
85 if(graphicsPipeline)
86 {
87 SPtr<GpuProgram> vertProgram = graphicsPipeline->getVertexProgram();
88 if (vertProgram)
89 {
90 vertProgram->blockUntilCoreInitialized();
91 allParamDescs.push_back(vertProgram->getParamDesc());
92 }
93
94 SPtr<GpuProgram> fragProgram = graphicsPipeline->getFragmentProgram();
95 if (fragProgram)
96 {
97 fragProgram->blockUntilCoreInitialized();
98 allParamDescs.push_back(fragProgram->getParamDesc());
99 }
100
101 SPtr<GpuProgram> geomProgram = graphicsPipeline->getGeometryProgram();
102 if (geomProgram)
103 {
104 geomProgram->blockUntilCoreInitialized();
105 allParamDescs.push_back(geomProgram->getParamDesc());
106 }
107
108 SPtr<GpuProgram> hullProgram = graphicsPipeline->getHullProgram();
109 if (hullProgram)
110 {
111 hullProgram->blockUntilCoreInitialized();
112 allParamDescs.push_back(hullProgram->getParamDesc());
113 }
114
115 SPtr<GpuProgram> domainProgram = graphicsPipeline->getDomainProgram();
116 if (domainProgram)
117 {
118 domainProgram->blockUntilCoreInitialized();
119 allParamDescs.push_back(domainProgram->getParamDesc());
120 }
121 }
122
123 const SPtr<ComputePipelineState>& computePipeline = curPass->getComputePipelineState();
124 if(computePipeline)
125 {
126 SPtr<GpuProgram> computeProgram = computePipeline->getProgram();
127 if (computeProgram)
128 {
129 computeProgram->blockUntilCoreInitialized();
130 allParamDescs.push_back(computeProgram->getParamDesc());
131 }
132
133 }
134 }
135
136 return allParamDescs;
137 }
138
139 Vector<SPtr<GpuParamDesc>> getAllParamDescs(const SPtr<ct::Technique>& technique)
140 {
141 Vector<SPtr<GpuParamDesc>> allParamDescs;
142
143 // Make sure all gpu programs are fully loaded
144 for (UINT32 i = 0; i < technique->getNumPasses(); i++)
145 {
146 SPtr<ct::Pass> curPass = technique->getPass(i);
147
148 const SPtr<ct::GraphicsPipelineState>& graphicsPipeline = curPass->getGraphicsPipelineState();
149 if(graphicsPipeline)
150 {
151 SPtr<ct::GpuProgram> vertProgram = graphicsPipeline->getVertexProgram();
152 if (vertProgram)
153 allParamDescs.push_back(vertProgram->getParamDesc());
154
155 SPtr<ct::GpuProgram> fragProgram = graphicsPipeline->getFragmentProgram();
156 if (fragProgram)
157 allParamDescs.push_back(fragProgram->getParamDesc());
158
159 SPtr<ct::GpuProgram> geomProgram = graphicsPipeline->getGeometryProgram();
160 if (geomProgram)
161 allParamDescs.push_back(geomProgram->getParamDesc());
162
163 SPtr<ct::GpuProgram> hullProgram = graphicsPipeline->getHullProgram();
164 if (hullProgram)
165 allParamDescs.push_back(hullProgram->getParamDesc());
166
167 SPtr<ct::GpuProgram> domainProgram = graphicsPipeline->getDomainProgram();
168 if (domainProgram)
169 allParamDescs.push_back(domainProgram->getParamDesc());
170 }
171
172 const SPtr<ct::ComputePipelineState>& computePipeline = curPass->getComputePipelineState();
173 if(computePipeline)
174 {
175 SPtr<ct::GpuProgram> computeProgram = computePipeline->getProgram();
176 if (computeProgram)
177 allParamDescs.push_back(computeProgram->getParamDesc());
178
179 }
180 }
181
182 return allParamDescs;
183 }
184
185 bool areParamsEqual(const GpuParamDataDesc& paramA, const GpuParamDataDesc& paramB, bool ignoreBufferOffsets)
186 {
187 bool equal = paramA.arraySize == paramB.arraySize && paramA.elementSize == paramB.elementSize
188 && paramA.type == paramB.type && paramA.arrayElementStride == paramB.arrayElementStride;
189
190 if (!ignoreBufferOffsets)
191 equal &= paramA.cpuMemOffset == paramB.cpuMemOffset && paramA.gpuMemOffset == paramB.gpuMemOffset;
192
193 return equal;
194 }
195
196 Vector<ShaderBlockDesc> determineValidShareableParamBlocks(const Vector<SPtr<GpuParamDesc>>& paramDescs,
197 const Map<String, SHADER_PARAM_BLOCK_DESC>& shaderDesc)
198 {
199 struct BlockInfo
200 {
201 BlockInfo() { }
202 BlockInfo(const GpuParamBlockDesc* blockDesc, const SPtr<GpuParamDesc>& paramDesc, bool isValid = true)
203 :blockDesc(blockDesc), paramDesc(paramDesc), isValid(isValid)
204 { }
205
206 const GpuParamBlockDesc* blockDesc;
207 SPtr<GpuParamDesc> paramDesc;
208 bool isValid;
209 UINT32 set;
210 UINT32 slot;
211 };
212
213 // Make sure param blocks with the same name actually contain the same fields
214 Map<String, BlockInfo> uniqueParamBlocks;
215
216 for (auto iter = paramDescs.begin(); iter != paramDescs.end(); ++iter)
217 {
218 const GpuParamDesc& curDesc = **iter;
219 for (auto blockIter = curDesc.paramBlocks.begin(); blockIter != curDesc.paramBlocks.end(); ++blockIter)
220 {
221 const GpuParamBlockDesc& curBlock = blockIter->second;
222
223 if (!curBlock.isShareable) // Non-shareable buffers are handled differently, they're allowed same names
224 continue;
225
226 auto iterFind = uniqueParamBlocks.find(blockIter->first);
227 if (iterFind == uniqueParamBlocks.end())
228 {
229 uniqueParamBlocks[blockIter->first] = BlockInfo(&curBlock, *iter);
230 continue;
231 }
232
233 const GpuParamBlockDesc& otherBlock = *iterFind->second.blockDesc;
234
235 // The block was already determined as invalid, no need to check further
236 if (!iterFind->second.isValid)
237 continue;
238
239 String otherBlockName = otherBlock.name;
240 SPtr<GpuParamDesc> otherDesc = iterFind->second.paramDesc;
241
242 for (auto myParamIter = curDesc.params.begin(); myParamIter != curDesc.params.end(); ++myParamIter)
243 {
244 const GpuParamDataDesc& myParam = myParamIter->second;
245
246 if (myParam.paramBlockSet != curBlock.set || myParam.paramBlockSlot != curBlock.slot)
247 continue; // Param is in another block, so we will check it when its time for that block
248
249 auto otherParamFind = otherDesc->params.find(myParamIter->first);
250
251 // Cannot find other param, blocks aren't equal
252 if (otherParamFind == otherDesc->params.end())
253 break;
254
255 const GpuParamDataDesc& otherParam = otherParamFind->second;
256
257 if (!areParamsEqual(myParam, otherParam, false) || curBlock.name != otherBlockName)
258 break;
259 }
260
261 // Note: Ignoring mismatched blocks for now, because glslang parser doesn't report dead uniform entries,
262 // meaning identical blocks can have different sets of uniforms reported depending on which are unused.
263 //if (!isBlockValid)
264 //{
265 // LOGWRN("Found two param blocks with the same name but different contents: " + blockIter->first);
266 // uniqueParamBlocks[blockIter->first] = BlockInfo(&curBlock, nullptr, false);
267
268 // continue;
269 //}
270 }
271 }
272
273 Vector<ShaderBlockDesc> output;
274 for (auto& entry : uniqueParamBlocks)
275 {
276 if (!entry.second.isValid)
277 continue;
278
279 const GpuParamBlockDesc& curBlock = *entry.second.blockDesc;
280
281 ShaderBlockDesc shaderBlockDesc;
282 shaderBlockDesc.external = false;
283 shaderBlockDesc.usage = GBU_STATIC;
284 shaderBlockDesc.size = curBlock.blockSize * sizeof(UINT32);
285 shaderBlockDesc.name = entry.first;
286 shaderBlockDesc.set = curBlock.set;
287 shaderBlockDesc.slot = curBlock.slot;
288
289 auto iterFind = shaderDesc.find(entry.first);
290 if (iterFind != shaderDesc.end())
291 {
292 shaderBlockDesc.external = iterFind->second.shared || iterFind->second.rendererSemantic != StringID::NONE;
293 shaderBlockDesc.usage = iterFind->second.usage;
294 }
295
296 output.push_back(shaderBlockDesc);
297 }
298
299 return output;
300 }
301
302 Map<String, const GpuParamDataDesc*> determineValidDataParameters(const Vector<SPtr<GpuParamDesc>>& paramDescs)
303 {
304 Map<String, const GpuParamDataDesc*> foundDataParams;
305 Map<String, bool> validParams;
306
307 for (auto iter = paramDescs.begin(); iter != paramDescs.end(); ++iter)
308 {
309 const GpuParamDesc& curDesc = **iter;
310
311 // Check regular data params
312 for (auto iter2 = curDesc.params.begin(); iter2 != curDesc.params.end(); ++iter2)
313 {
314 const GpuParamDataDesc& curParam = iter2->second;
315
316 auto dataFindIter = validParams.find(iter2->first);
317 if (dataFindIter == validParams.end())
318 {
319 validParams[iter2->first] = true;
320 foundDataParams[iter2->first] = &curParam;
321 }
322 else
323 {
324 if (validParams[iter2->first])
325 {
326 auto dataFindIter2 = foundDataParams.find(iter2->first);
327
328 const GpuParamDataDesc* otherParam = dataFindIter2->second;
329 if (!areParamsEqual(curParam, *otherParam, true))
330 {
331 validParams[iter2->first] = false;
332 foundDataParams.erase(dataFindIter2);
333 }
334 }
335 }
336 }
337 }
338
339 return foundDataParams;
340 }
341
342 Vector<const GpuParamObjectDesc*> determineValidObjectParameters(const Vector<SPtr<GpuParamDesc>>& paramDescs)
343 {
344 Vector<const GpuParamObjectDesc*> validParams;
345
346 for (auto iter = paramDescs.begin(); iter != paramDescs.end(); ++iter)
347 {
348 const GpuParamDesc& curDesc = **iter;
349
350 // Check sampler params
351 for (auto iter2 = curDesc.samplers.begin(); iter2 != curDesc.samplers.end(); ++iter2)
352 {
353 validParams.push_back(&iter2->second);
354 }
355
356 // Check texture params
357 for (auto iter2 = curDesc.textures.begin(); iter2 != curDesc.textures.end(); ++iter2)
358 {
359 validParams.push_back(&iter2->second);
360 }
361
362 // Check load-store texture params
363 for (auto iter2 = curDesc.loadStoreTextures.begin(); iter2 != curDesc.loadStoreTextures.end(); ++iter2)
364 {
365 validParams.push_back(&iter2->second);
366 }
367
368 // Check buffer params
369 for (auto iter2 = curDesc.buffers.begin(); iter2 != curDesc.buffers.end(); ++iter2)
370 {
371 validParams.push_back(&iter2->second);
372 }
373 }
374
375 return validParams;
376 }
377
378 Map<String, String> determineParameterToBlockMapping(const Vector<SPtr<GpuParamDesc>>& paramDescs)
379 {
380 Map<String, String> paramToParamBlock;
381
382 for (auto iter = paramDescs.begin(); iter != paramDescs.end(); ++iter)
383 {
384 const GpuParamDesc& curDesc = **iter;
385 for (auto iter2 = curDesc.params.begin(); iter2 != curDesc.params.end(); ++iter2)
386 {
387 const GpuParamDataDesc& curParam = iter2->second;
388
389 auto iterFind = paramToParamBlock.find(curParam.name);
390 if (iterFind != paramToParamBlock.end())
391 continue;
392
393 for (auto iterBlock = curDesc.paramBlocks.begin(); iterBlock != curDesc.paramBlocks.end(); ++iterBlock)
394 {
395 if (iterBlock->second.set == curParam.paramBlockSet && iterBlock->second.slot == curParam.paramBlockSlot)
396 {
397 paramToParamBlock[curParam.name] = iterBlock->second.name;
398 break;
399 }
400 }
401 }
402 }
403
404 return paramToParamBlock;
405 }
406
407 UnorderedMap<ValidParamKey, String> determineValidParameters(const Vector<SPtr<GpuParamDesc>>& paramDescs,
408 const Map<String, SHADER_DATA_PARAM_DESC>& dataParams,
409 const Map<String, SHADER_OBJECT_PARAM_DESC>& textureParams,
410 const Map<String, SHADER_OBJECT_PARAM_DESC>& bufferParams,
411 const Map<String, SHADER_OBJECT_PARAM_DESC>& samplerParams)
412 {
413 UnorderedMap<ValidParamKey, String> validParams;
414
415 Map<String, const GpuParamDataDesc*> validDataParameters = determineValidDataParameters(paramDescs);
416 Vector<const GpuParamObjectDesc*> validObjectParameters = determineValidObjectParameters(paramDescs);
417 Map<String, String> paramToParamBlockMap = determineParameterToBlockMapping(paramDescs);
418
419 // Create data param mappings
420 for (auto iter = dataParams.begin(); iter != dataParams.end(); ++iter)
421 {
422 auto findIter = validDataParameters.find(iter->second.gpuVariableName);
423
424 // Not valid so we skip it
425 if (findIter == validDataParameters.end())
426 continue;
427
428 if (findIter->second->type != iter->second.type &&
429 !(iter->second.type == GPDT_COLOR && (findIter->second->type == GPDT_FLOAT4 || findIter->second->type == GPDT_FLOAT3)))
430 {
431 LOGWRN("Ignoring shader parameter \"" + iter->first + "\". Type doesn't match the one defined in the gpu program. "
432 + "Shader defined type: " + toString(iter->second.type) + " - Gpu program defined type: " + toString(findIter->second->type));
433 continue;
434 }
435
436 auto findBlockIter = paramToParamBlockMap.find(iter->second.gpuVariableName);
437
438 if (findBlockIter == paramToParamBlockMap.end())
439 BS_EXCEPT(InternalErrorException, "Parameter doesn't exist in param to param block map but exists in valid param map.");
440
441 ValidParamKey key(iter->second.gpuVariableName, MaterialParams::ParamType::Data);
442 validParams.insert(std::make_pair(key, iter->first));
443 }
444
445 // Create object param mappings
446 auto determineObjectMappings = [&](const Map<String, SHADER_OBJECT_PARAM_DESC>& params, MaterialParams::ParamType paramType)
447 {
448 for (auto iter = params.begin(); iter != params.end(); ++iter)
449 {
450 const Vector<String>& gpuVariableNames = iter->second.gpuVariableNames;
451 for (auto iter2 = gpuVariableNames.begin(); iter2 != gpuVariableNames.end(); ++iter2)
452 {
453 for (auto iter3 = validObjectParameters.begin(); iter3 != validObjectParameters.end(); ++iter3)
454 {
455 if ((*iter3)->name == (*iter2))
456 {
457 ValidParamKey key(*iter2, paramType);
458 validParams.insert(std::make_pair(key, iter->first));
459
460 break;
461 }
462 }
463 }
464 }
465 };
466
467 determineObjectMappings(textureParams, MaterialParams::ParamType::Texture);
468 determineObjectMappings(samplerParams, MaterialParams::ParamType::Sampler);
469 determineObjectMappings(bufferParams, MaterialParams::ParamType::Buffer);
470
471 return validParams;
472 }
473
474 template<bool Core>
475 const UINT32 TGpuParamsSet<Core>::NUM_STAGES = 6;
476
477 template<bool Core>
478 TGpuParamsSet<Core>::TGpuParamsSet(const SPtr<TechniqueType>& technique, const ShaderType& shader,
479 const SPtr<MaterialParamsType>& params)
480 :mPassParams(technique->getNumPasses()), mParamVersion(0)
481 {
482 UINT32 numPasses = technique->getNumPasses();
483
484 // Create GpuParams for each pass and shader stage
485 for (UINT32 i = 0; i < numPasses; i++)
486 {
487 SPtr<PassType> curPass = technique->getPass(i);
488
489 SPtr<GraphicsPipelineStateType> gfxPipeline = curPass->getGraphicsPipelineState();
490 if(gfxPipeline != nullptr)
491 mPassParams[i] = GpuParamsType::create(gfxPipeline);
492 else
493 {
494 SPtr<ComputePipelineStateType> computePipeline = curPass->getComputePipelineState();
495 mPassParams[i] = GpuParamsType::create(computePipeline);
496 }
497 }
498
499 // Create and assign parameter block buffers
500 Vector<SPtr<GpuParamDesc>> allParamDescs = getAllParamDescs(technique);
501
502 //// Fill out various helper structures
503 Vector<ShaderBlockDesc> paramBlockData = determineValidShareableParamBlocks(allParamDescs, shader->getParamBlocks());
504 UnorderedMap<ValidParamKey, String> validParams = determineValidParameters(
505 allParamDescs,
506 shader->getDataParams(),
507 shader->getTextureParams(),
508 shader->getBufferParams(),
509 shader->getSamplerParams());
510
511 Map<String, ParamBlockPtrType> paramBlockBuffers;
512
513 //// Create param blocks
514 for (auto& paramBlock : paramBlockData)
515 {
516 ParamBlockPtrType newParamBlockBuffer;
517 if (!paramBlock.external)
518 newParamBlockBuffer = ParamBlockType::create(paramBlock.size, paramBlock.usage);
519
520 paramBlock.sequentialIdx = (UINT32)mBlocks.size();
521
522 paramBlockBuffers[paramBlock.name] = newParamBlockBuffer;
523 mBlocks.push_back(BlockInfo(paramBlock.name, paramBlock.set, paramBlock.slot, newParamBlockBuffer, true));
524 }
525
526 //// Assign param block buffers and generate information about data parameters
527 assert(numPasses < 64); // BlockInfo flags uses UINT64 for tracking usage
528 for (UINT32 i = 0; i < numPasses; i++)
529 {
530 SPtr<GpuParamsType> paramPtr = mPassParams[i];
531 for (UINT32 j = 0; j < NUM_STAGES; j++)
532 {
533 GpuProgramType progType = (GpuProgramType)j;
534
535 // Assign shareable buffers
536 for (auto& block : paramBlockData)
537 {
538 const String& paramBlockName = block.name;
539 if (paramPtr->hasParamBlock(progType, paramBlockName))
540 {
541 ParamBlockPtrType blockBuffer = paramBlockBuffers[paramBlockName];
542
543 paramPtr->setParamBlockBuffer(progType, paramBlockName, blockBuffer);
544 }
545 }
546
547 // Create non-shareable ones (these are buffers defined by default by the RHI usually)
548 SPtr<GpuParamDesc> desc = paramPtr->getParamDesc(progType);
549 if (desc == nullptr)
550 continue;
551
552 for (auto iterBlockDesc = desc->paramBlocks.begin(); iterBlockDesc != desc->paramBlocks.end(); ++iterBlockDesc)
553 {
554 const GpuParamBlockDesc& blockDesc = iterBlockDesc->second;
555
556 UINT32 globalBlockIdx = (UINT32)-1;
557 if (!blockDesc.isShareable)
558 {
559 ParamBlockPtrType newParamBlockBuffer = ParamBlockType::create(blockDesc.blockSize * sizeof(UINT32));
560
561 globalBlockIdx = (UINT32)mBlocks.size();
562
563 paramPtr->setParamBlockBuffer(progType, iterBlockDesc->first, newParamBlockBuffer);
564 mBlocks.emplace_back(iterBlockDesc->first, iterBlockDesc->second.set,
565 iterBlockDesc->second.slot, newParamBlockBuffer, false);
566 }
567 else
568 {
569 auto iterFind = std::find_if(paramBlockData.begin(), paramBlockData.end(), [&](const auto& x)
570 {
571 return x.name == iterBlockDesc->first;
572 });
573
574 if(iterFind != paramBlockData.end())
575 globalBlockIdx = iterFind->sequentialIdx;
576 }
577
578 // If this parameter block is valid, create data/struct mappings for it
579 if (globalBlockIdx == (UINT32)-1)
580 continue;
581
582 for(auto& dataParam : desc->params)
583 {
584 if (dataParam.second.paramBlockSet != blockDesc.set || dataParam.second.paramBlockSlot != blockDesc.slot)
585 continue;
586
587 ValidParamKey key(dataParam.first, MaterialParams::ParamType::Data);
588
589 auto iterFind = validParams.find(key);
590 if (iterFind == validParams.end())
591 continue;
592
593 UINT32 paramIdx = params->getParamIndex(iterFind->second);
594
595 // Parameter shouldn't be in the valid parameter list if it cannot be found
596 assert(paramIdx != (UINT32)-1);
597
598 mDataParamInfos.push_back(DataParamInfo());
599 DataParamInfo& paramInfo = mDataParamInfos.back();
600 paramInfo.paramIdx = paramIdx;
601 paramInfo.blockIdx = globalBlockIdx;
602 paramInfo.offset = dataParam.second.cpuMemOffset;
603 paramInfo.arrayStride = dataParam.second.arrayElementStride;
604 }
605 }
606 }
607 }
608
609 // Add buffers defined in shader but not actually used by GPU programs (so we can check if user is providing a
610 // valid buffer name)
611 auto& allParamBlocks = shader->getParamBlocks();
612 for (auto& entry : allParamBlocks)
613 {
614 auto iterFind = std::find_if(mBlocks.begin(), mBlocks.end(),
615 [&](auto& x)
616 {
617 return x.name == entry.first;
618 });
619
620 if(iterFind == mBlocks.end())
621 {
622 mBlocks.push_back(BlockInfo(entry.first, 0, 0, nullptr, true));
623 mBlocks.back().isUsed = false;
624 }
625 }
626
627 // Generate information about object parameters
628 bs_frame_mark();
629 {
630 FrameVector<ObjectParamInfo> objParamInfos;
631
632 UINT32 offsetsSize = numPasses * NUM_STAGES * 4 * sizeof(UINT32);
633 UINT32* offsets = (UINT32*)bs_frame_alloc(offsetsSize);
634 memset(offsets, 0, offsetsSize);
635
636 // First store all objects in temporary arrays since we don't know how many of them are
637 UINT32 totalNumObjects = 0;
638 UINT32* stageOffsets = offsets;
639 for (UINT32 i = 0; i < numPasses; i++)
640 {
641 SPtr<GpuParamsType> paramPtr = mPassParams[i];
642 for (UINT32 j = 0; j < NUM_STAGES; j++)
643 {
644 GpuProgramType progType = (GpuProgramType)j;
645
646 auto processObjectParams = [&](const Map<String, GpuParamObjectDesc>& gpuParams,
647 UINT32 stageIdx, MaterialParams::ParamType paramType)
648 {
649 for (auto& param : gpuParams)
650 {
651 ValidParamKey key(param.first, paramType);
652
653 auto iterFind = validParams.find(key);
654 if (iterFind == validParams.end())
655 continue;
656
657 UINT32 paramIdx;
658 auto result = params->getParamIndex(iterFind->second, paramType, GPDT_UNKNOWN, 0, paramIdx);
659
660 // Parameter shouldn't be in the valid parameter list if it cannot be found
661 assert(result == MaterialParams::GetParamResult::Success);
662
663 objParamInfos.push_back(ObjectParamInfo());
664 ObjectParamInfo& paramInfo = objParamInfos.back();
665 paramInfo.paramIdx = paramIdx;
666 paramInfo.slotIdx = param.second.slot;
667 paramInfo.setIdx = param.second.set;
668
669 stageOffsets[stageIdx]++;
670 totalNumObjects++;
671 }
672 };
673
674 SPtr<GpuParamDesc> desc = paramPtr->getParamDesc(progType);
675 if(desc == nullptr)
676 {
677 stageOffsets += 4;
678 continue;
679 }
680
681 processObjectParams(desc->textures, 0, MaterialParams::ParamType::Texture);
682 processObjectParams(desc->loadStoreTextures, 1, MaterialParams::ParamType::Texture);
683 processObjectParams(desc->buffers, 2, MaterialParams::ParamType::Buffer);
684 processObjectParams(desc->samplers, 3, MaterialParams::ParamType::Sampler);
685
686 stageOffsets += 4;
687 }
688 }
689
690 // Transfer all objects into their permanent storage
691 UINT32 numBlocks = (UINT32)mBlocks.size();
692 UINT32 blockBindingsSize = numBlocks * numPasses * sizeof(PassBlockBindings);
693 UINT32 objectParamInfosSize = totalNumObjects * sizeof(ObjectParamInfo) + numPasses * sizeof(PassParamInfo);
694 mData = (UINT8*)bs_alloc(objectParamInfosSize + blockBindingsSize);
695 UINT8* dataIter = mData;
696
697 mPassParamInfos = (PassParamInfo*)dataIter;
698 memset(mPassParamInfos, 0, objectParamInfosSize);
699 dataIter += objectParamInfosSize;
700
701 StageParamInfo* stageInfos = (StageParamInfo*)mPassParamInfos;
702
703 ObjectParamInfo* objInfos = (ObjectParamInfo*)(mPassParamInfos + numPasses);
704 memcpy(objInfos, objParamInfos.data(), totalNumObjects * sizeof(ObjectParamInfo));
705
706 UINT32 objInfoOffset = 0;
707
708 stageOffsets = offsets;
709 for (UINT32 i = 0; i < numPasses; i++)
710 {
711 for (UINT32 j = 0; j < NUM_STAGES; j++)
712 {
713 StageParamInfo& stage = stageInfos[i * NUM_STAGES + j];
714
715 if(stageOffsets[0] > 0)
716 {
717 UINT32 numEntries = stageOffsets[0];
718
719 stage.textures = objInfos + objInfoOffset;
720 stage.numTextures = numEntries;
721
722 objInfoOffset += numEntries;
723 }
724
725 if (stageOffsets[1] > 0)
726 {
727 UINT32 numEntries = stageOffsets[1];
728
729 stage.loadStoreTextures = objInfos + objInfoOffset;
730 stage.numLoadStoreTextures = numEntries;
731
732 objInfoOffset += numEntries;
733 }
734
735 if (stageOffsets[2] > 0)
736 {
737 UINT32 numEntries = stageOffsets[2];
738
739 stage.buffers = objInfos + objInfoOffset;
740 stage.numBuffers = numEntries;
741
742 objInfoOffset += numEntries;
743 }
744
745 if (stageOffsets[3] > 0)
746 {
747 UINT32 numEntries = stageOffsets[3];
748
749 stage.samplerStates = objInfos + objInfoOffset;
750 stage.numSamplerStates = numEntries;
751
752 objInfoOffset += numEntries;
753 }
754
755 stageOffsets += 4;
756 }
757 }
758
759 // Determine on which passes & stages are buffers used on
760 for (auto& block : mBlocks)
761 {
762 block.passData = (PassBlockBindings*)dataIter;
763 dataIter += sizeof(PassBlockBindings) * numPasses;
764 }
765
766 for (auto& block : mBlocks)
767 {
768 for (UINT32 i = 0; i < numPasses; i++)
769 {
770 SPtr<GpuParamsType> paramPtr = mPassParams[i];
771 for (UINT32 j = 0; j < NUM_STAGES; j++)
772 {
773 GpuProgramType progType = (GpuProgramType)j;
774
775 SPtr<GpuParamDesc> curDesc = paramPtr->getParamDesc(progType);
776 if (curDesc == nullptr)
777 {
778 block.passData[i].bindings[j].set = -1;
779 block.passData[i].bindings[j].slot = -1;
780
781 continue;
782 }
783
784 auto iterFind = curDesc->paramBlocks.find(block.name);
785 if (iterFind == curDesc->paramBlocks.end())
786 {
787 block.passData[i].bindings[j].set = -1;
788 block.passData[i].bindings[j].slot = -1;
789
790 continue;
791 }
792
793 block.passData[i].bindings[j].set = iterFind->second.set;
794 block.passData[i].bindings[j].slot = iterFind->second.slot;
795 }
796 }
797 }
798
799 bs_frame_free(offsets);
800 }
801 bs_frame_clear();
802 }
803
804 template<bool Core>
805 TGpuParamsSet<Core>::~TGpuParamsSet()
806 {
807 // All allocations share the same memory, so we just clear it all at once
808 bs_free(mData);
809 }
810
811 template<bool Core>
812 SPtr<typename TGpuParamsSet<Core>::GpuParamsType> TGpuParamsSet<Core>::getGpuParams(UINT32 passIdx)
813 {
814 if (passIdx >= mPassParams.size())
815 return nullptr;
816
817 return mPassParams[passIdx];
818 }
819
820 template<bool Core>
821 UINT32 TGpuParamsSet<Core>::getParamBlockBufferIndex(const String& name) const
822 {
823 for (UINT32 i = 0; i < (UINT32)mBlocks.size(); i++)
824 {
825 const BlockInfo& block = mBlocks[i];
826 if (block.name == name)
827 return i;
828 }
829
830 return -1;
831 }
832
833 template<bool Core>
834 void TGpuParamsSet<Core>::setParamBlockBuffer(UINT32 index, const ParamBlockPtrType& paramBlock,
835 bool ignoreInUpdate)
836 {
837 BlockInfo& blockInfo = mBlocks[index];
838 if (!blockInfo.shareable)
839 {
840 LOGERR("Cannot set parameter block buffer with the name \"" + blockInfo.name +
841 "\". Buffer is not assignable. ");
842 return;
843 }
844
845 if (!blockInfo.isUsed)
846 return;
847
848 blockInfo.allowUpdate = !ignoreInUpdate;
849
850 if (blockInfo.buffer != paramBlock)
851 {
852 blockInfo.buffer = paramBlock;
853
854 UINT32 numPasses = (UINT32)mPassParams.size();
855 for (UINT32 j = 0; j < numPasses; j++)
856 {
857 SPtr<GpuParamsType> paramPtr = mPassParams[j];
858 for (UINT32 i = 0; i < NUM_STAGES; i++)
859 {
860 GpuProgramType progType = (GpuProgramType)i;
861
862 const BlockBinding& binding = blockInfo.passData[j].bindings[progType];
863
864 if (binding.slot != (UINT32)-1)
865 paramPtr->setParamBlockBuffer(binding.set, binding.slot, paramBlock);
866 }
867 }
868 }
869 }
870
871 template<bool Core>
872 void TGpuParamsSet<Core>::setParamBlockBuffer(const String& name, const ParamBlockPtrType& paramBlock,
873 bool ignoreInUpdate)
874 {
875 UINT32 bufferIdx = getParamBlockBufferIndex(name);
876 if(bufferIdx == (UINT32)-1)
877 {
878 LOGERR("Cannot set parameter block buffer with the name \"" + name + "\". Buffer name not found. ");
879 return;
880 }
881
882 setParamBlockBuffer(bufferIdx, paramBlock, ignoreInUpdate);
883 }
884
885 template<bool Core>
886 void TGpuParamsSet<Core>::update(const SPtr<MaterialParamsType>& params, float t, bool updateAll)
887 {
888 // Note: Instead of iterating over every single parameter, it might be more efficient for @p params to keep
889 // a ring buffer and a version number. Then we could just iterate over the ring buffer and only access dirty
890 // parameters. If the version number is too high (larger than ring buffer can store), then we force update for all.
891
892 // Update data params
893 for(auto& paramInfo : mDataParamInfos)
894 {
895 ParamBlockPtrType paramBlock = mBlocks[paramInfo.blockIdx].buffer;
896 if (paramBlock == nullptr || !mBlocks[paramInfo.blockIdx].allowUpdate)
897 continue;
898
899 const MaterialParams::ParamData* materialParamInfo = params->getParamData(paramInfo.paramIdx);
900 UINT32 arraySize = materialParamInfo->arraySize == 0 ? 1 : materialParamInfo->arraySize;
901
902 bool isAnimated = false;
903 for(UINT32 i = 0; i < arraySize; i++)
904 {
905 isAnimated = params->isAnimated(*materialParamInfo, i);
906 if(isAnimated)
907 break;
908 }
909
910 if (materialParamInfo->version <= mParamVersion && !updateAll && !isAnimated)
911 continue;
912
913 if(materialParamInfo->dataType != GPDT_STRUCT)
914 {
915 const GpuParamDataTypeInfo& typeInfo = GpuParams::PARAM_SIZES.lookup[(int)materialParamInfo->dataType];
916 UINT32 paramSize = typeInfo.numColumns * typeInfo.numRows * typeInfo.baseTypeSize;
917
918 UINT8* data = params->getData(materialParamInfo->index);
919 if (!isAnimated)
920 {
921 const bool transposeMatrices = ct::gCaps().conventions.matrixOrder == Conventions::MatrixOrder::ColumnMajor;
922 if (transposeMatrices)
923 {
924 auto writeTransposed = [&paramInfo, &paramSize, &arraySize, &paramBlock, data](auto& temp)
925 {
926 for (UINT32 i = 0; i < arraySize; i++)
927 {
928 UINT32 readOffset = i * paramSize;
929 memcpy(&temp, data + readOffset, paramSize);
930 auto transposed = temp.transpose();
931
932 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
933 paramBlock->write(writeOffset, &transposed, paramSize);
934 }
935 };
936
937 switch (materialParamInfo->dataType)
938 {
939 case GPDT_MATRIX_2X2:
940 {
941 MatrixNxM<2, 2> matrix;
942 writeTransposed(matrix);
943 }
944 break;
945 case GPDT_MATRIX_2X3:
946 {
947 MatrixNxM<2, 3> matrix;
948 writeTransposed(matrix);
949 }
950 break;
951 case GPDT_MATRIX_2X4:
952 {
953 MatrixNxM<2, 4> matrix;
954 writeTransposed(matrix);
955 }
956 break;
957 case GPDT_MATRIX_3X2:
958 {
959 MatrixNxM<3, 2> matrix;
960 writeTransposed(matrix);
961 }
962 break;
963 case GPDT_MATRIX_3X3:
964 {
965 Matrix3 matrix;
966 writeTransposed(matrix);
967 }
968 break;
969 case GPDT_MATRIX_3X4:
970 {
971 MatrixNxM<3, 4> matrix;
972 writeTransposed(matrix);
973 }
974 break;
975 case GPDT_MATRIX_4X2:
976 {
977 MatrixNxM<4, 2> matrix;
978 writeTransposed(matrix);
979 }
980 break;
981 case GPDT_MATRIX_4X3:
982 {
983 MatrixNxM<4, 3> matrix;
984 writeTransposed(matrix);
985 }
986 break;
987 case GPDT_MATRIX_4X4:
988 {
989 Matrix4 matrix;
990 writeTransposed(matrix);
991 }
992 break;
993 default:
994 {
995 for (UINT32 i = 0; i < arraySize; i++)
996 {
997 UINT32 arrayOffset = i * paramSize;
998 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
999 paramBlock->write(writeOffset, data + arrayOffset, paramSize);
1000 }
1001 break;
1002 }
1003 }
1004 }
1005 else
1006 {
1007 for (UINT32 i = 0; i < arraySize; i++)
1008 {
1009 UINT32 readOffset = i * paramSize;
1010 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
1011 paramBlock->write(writeOffset, data + readOffset, paramSize);
1012 }
1013 }
1014 }
1015 else // Animated
1016 {
1017 if (materialParamInfo->dataType == GPDT_FLOAT1)
1018 {
1019 assert(paramSize == sizeof(float));
1020
1021 for (UINT32 i = 0; i < arraySize; i++)
1022 {
1023 UINT32 readOffset = i * paramSize;
1024 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
1025
1026 float value;
1027 if (params->isAnimated(*materialParamInfo, i))
1028 {
1029 const TAnimationCurve<float>& curve = params->template getCurveParam<float>(*materialParamInfo, i);
1030
1031 value = curve.evaluate(t, true);
1032 }
1033 else
1034 memcpy(&value, data + readOffset, paramSize);
1035
1036 paramBlock->write(writeOffset, &value, paramSize);
1037 }
1038 }
1039 else if (materialParamInfo->dataType == GPDT_FLOAT4)
1040 {
1041 assert(paramSize == sizeof(Rect2));
1042
1043 CoreVariantHandleType<SpriteTexture, Core> spriteTexture =
1044 params->getOwningSpriteTexture(*materialParamInfo);
1045
1046 UINT32 writeOffset = paramInfo.offset * sizeof(UINT32);
1047 Rect2 uv = Rect2(0.0f, 0.0f, 1.0f, 1.0f);
1048 if (spriteTexture != nullptr)
1049 uv = spriteTexture->evaluate(t);
1050
1051 paramBlock->write(writeOffset, &uv, paramSize);
1052
1053 // Only the first array element receives sprite UVs, the rest are treated as normal
1054 for (UINT32 i = 1; i < arraySize; i++)
1055 {
1056 UINT32 readOffset = i * paramSize;
1057 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
1058
1059 paramBlock->write(writeOffset, data + readOffset, paramSize);
1060 }
1061 }
1062 else if (materialParamInfo->dataType == GPDT_COLOR)
1063 {
1064 for (UINT32 i = 0; i < arraySize; i++)
1065 {
1066 assert(paramSize == sizeof(Color));
1067
1068 UINT32 readOffset = i * paramSize;
1069 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
1070
1071 Color value;
1072 if (params->isAnimated(*materialParamInfo, i))
1073 {
1074 const ColorGradient& gradient = params->getColorGradientParam(*materialParamInfo, i);
1075
1076 const float wrappedT = Math::repeat(t, gradient.getDuration());
1077 value = Color::fromRGBA(gradient.evaluate(wrappedT));
1078 }
1079 else
1080 memcpy(&value, data + readOffset, paramSize);
1081
1082 paramBlock->write(writeOffset, &value, paramSize);
1083 }
1084 }
1085 }
1086 }
1087 else
1088 {
1089 UINT32 paramSize = params->getStructSize(*materialParamInfo);
1090 void* paramData = bs_stack_alloc(paramSize);
1091 for (UINT32 i = 0; i < arraySize; i++)
1092 {
1093 params->getStructData(*materialParamInfo, paramData, paramSize, i);
1094
1095 UINT32 writeOffset = (paramInfo.offset + paramInfo.arrayStride * i) * sizeof(UINT32);
1096 paramBlock->write(writeOffset, paramData, paramSize);
1097 }
1098 bs_stack_free(paramData);
1099 }
1100 }
1101
1102 // Update object params
1103 const auto numPasses = (UINT32)mPassParams.size();
1104
1105 for(UINT32 i = 0; i < numPasses; i++)
1106 {
1107 SPtr<GpuParamsType> paramPtr = mPassParams[i];
1108
1109 for(UINT32 j = 0; j < NUM_STAGES; j++)
1110 {
1111 const StageParamInfo& stageInfo = mPassParamInfos[i].stages[j];
1112
1113 for(UINT32 k = 0; k < stageInfo.numTextures; k++)
1114 {
1115 const ObjectParamInfo& paramInfo = stageInfo.textures[k];
1116
1117 const MaterialParams::ParamData* materialParamInfo = params->getParamData(paramInfo.paramIdx);
1118 if (materialParamInfo->version <= mParamVersion && !updateAll)
1119 continue;
1120
1121 TextureSurface surface;
1122 TextureType texture;
1123 params->getTexture(*materialParamInfo, texture, surface);
1124
1125 paramPtr->setTexture(paramInfo.setIdx, paramInfo.slotIdx, texture, surface);
1126 }
1127
1128 for (UINT32 k = 0; k < stageInfo.numLoadStoreTextures; k++)
1129 {
1130 const ObjectParamInfo& paramInfo = stageInfo.loadStoreTextures[k];
1131
1132 const MaterialParams::ParamData* materialParamInfo = params->getParamData(paramInfo.paramIdx);
1133 if (materialParamInfo->version <= mParamVersion && !updateAll)
1134 continue;
1135
1136 TextureSurface surface;
1137 TextureType texture;
1138 params->getLoadStoreTexture(*materialParamInfo, texture, surface);
1139
1140 paramPtr->setLoadStoreTexture(paramInfo.setIdx, paramInfo.slotIdx, texture, surface);
1141 }
1142
1143 for (UINT32 k = 0; k < stageInfo.numBuffers; k++)
1144 {
1145 const ObjectParamInfo& paramInfo = stageInfo.buffers[k];
1146
1147 const MaterialParams::ParamData* materialParamInfo = params->getParamData(paramInfo.paramIdx);
1148 if (materialParamInfo->version <= mParamVersion && !updateAll)
1149 continue;
1150
1151 BufferType buffer;
1152 params->getBuffer(*materialParamInfo, buffer);
1153
1154 paramPtr->setBuffer(paramInfo.setIdx, paramInfo.slotIdx, buffer);
1155 }
1156
1157 for (UINT32 k = 0; k < stageInfo.numSamplerStates; k++)
1158 {
1159 const ObjectParamInfo& paramInfo = stageInfo.samplerStates[k];
1160
1161 const MaterialParams::ParamData* materialParamInfo = params->getParamData(paramInfo.paramIdx);
1162 if (materialParamInfo->version <= mParamVersion && !updateAll)
1163 continue;
1164
1165 SamplerStateType samplerState;
1166 params->getSamplerState(*materialParamInfo, samplerState);
1167
1168 paramPtr->setSamplerState(paramInfo.setIdx, paramInfo.slotIdx, samplerState);
1169 }
1170 }
1171
1172 paramPtr->_markCoreDirty();
1173 }
1174
1175 mParamVersion = params->getParamVersion();
1176 }
1177
1178 template class TGpuParamsSet <false>;
1179 template class TGpuParamsSet <true>;
1180}
1181