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 | |
17 | namespace 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 | |
43 | namespace 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 | |
62 | namespace 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 = [¶mInfo, ¶mSize, &arraySize, ¶mBlock, 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 | |