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 "GLSL/BsGLSLParamParser.h" |
4 | #include "RenderAPI/BsGpuParams.h" |
5 | |
6 | namespace bs { namespace ct |
7 | { |
8 | INT32 GLSLAttribute::matchesName(const String& name) |
9 | { |
10 | if (name.length() >= mName.length()) |
11 | { |
12 | if (name.substr(0, mName.length()) == mName) |
13 | { |
14 | String indexStr = name.substr(mName.length(), name.length()); |
15 | return parseUINT32(indexStr, 0); |
16 | } |
17 | } |
18 | |
19 | return -1; |
20 | } |
21 | |
22 | Vector<VertexElement> GLSLParamParser::buildVertexDeclaration(GLuint glProgram) |
23 | { |
24 | GLint numAttributes = 0; |
25 | glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &numAttributes); |
26 | BS_CHECK_GL_ERROR(); |
27 | |
28 | GLint maxNameSize = 0; |
29 | glGetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameSize); |
30 | BS_CHECK_GL_ERROR(); |
31 | |
32 | GLchar* attributeName = (GLchar*)bs_alloc(sizeof(GLchar)* maxNameSize); |
33 | |
34 | Vector<VertexElement> elementList; |
35 | for (GLint i = 0; i < numAttributes; i++) |
36 | { |
37 | GLint attribSize = 0; |
38 | GLenum attribType = 0; |
39 | glGetActiveAttrib(glProgram, i, maxNameSize, nullptr, &attribSize, &attribType, attributeName); |
40 | BS_CHECK_GL_ERROR(); |
41 | |
42 | VertexElementSemantic semantic = VES_POSITION; |
43 | UINT16 index = 0; |
44 | if (attribNameToElementSemantic(attributeName, semantic, index)) |
45 | { |
46 | VertexElementType type = glTypeToAttributeType(attribType); |
47 | UINT32 slot = glGetAttribLocation(glProgram, attributeName); |
48 | BS_CHECK_GL_ERROR(); |
49 | |
50 | elementList.push_back(VertexElement(0, slot, type, semantic, index)); |
51 | } |
52 | else |
53 | { |
54 | // Ignore built-in attributes |
55 | if(memcmp(attributeName, "gl_" , 3) != 0) |
56 | LOGWRN("Cannot determine vertex input attribute type for attribute: " + String(attributeName)); |
57 | } |
58 | } |
59 | |
60 | bs_free(attributeName); |
61 | |
62 | return elementList; |
63 | } |
64 | |
65 | UINT32 GLSLParamParser::calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset) |
66 | { |
67 | const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[type]; |
68 | UINT32 size = (typeInfo.baseTypeSize * typeInfo.numColumns * typeInfo.numRows) / 4; |
69 | UINT32 alignment = typeInfo.alignment / 4; |
70 | |
71 | // Fix alignment if needed |
72 | UINT32 alignOffset = offset % alignment; |
73 | if (alignOffset != 0) |
74 | { |
75 | UINT32 padding = (alignment - alignOffset); |
76 | offset += padding; |
77 | } |
78 | |
79 | if (arraySize > 1) |
80 | { |
81 | // Array elements are always padded and aligned to vec4 |
82 | alignOffset = size % 4; |
83 | if (alignOffset != 0) |
84 | { |
85 | UINT32 padding = (4 - alignOffset); |
86 | size += padding; |
87 | } |
88 | |
89 | alignOffset = offset % 4; |
90 | if (alignOffset != 0) |
91 | { |
92 | UINT32 padding = (4 - alignOffset); |
93 | offset += padding; |
94 | } |
95 | |
96 | return size; |
97 | } |
98 | else |
99 | return size; |
100 | } |
101 | |
102 | VertexElementType GLSLParamParser::glTypeToAttributeType(GLenum glType) |
103 | { |
104 | switch (glType) |
105 | { |
106 | case GL_FLOAT: |
107 | return VET_FLOAT1; |
108 | case GL_FLOAT_VEC2: |
109 | return VET_FLOAT2; |
110 | case GL_FLOAT_VEC3: |
111 | return VET_FLOAT3; |
112 | case GL_FLOAT_VEC4: |
113 | return VET_FLOAT4; |
114 | case GL_INT: |
115 | return VET_INT1; |
116 | case GL_INT_VEC2: |
117 | return VET_INT2; |
118 | case GL_INT_VEC3: |
119 | return VET_INT3; |
120 | case GL_INT_VEC4: |
121 | return VET_INT4; |
122 | case GL_UNSIGNED_INT: |
123 | return VET_UINT1; |
124 | case GL_UNSIGNED_INT_VEC2: |
125 | return VET_UINT2; |
126 | case GL_UNSIGNED_INT_VEC3: |
127 | return VET_UINT3; |
128 | case GL_UNSIGNED_INT_VEC4: |
129 | return VET_UINT4; |
130 | default: |
131 | BS_EXCEPT(NotImplementedException, "Unsupported vertex attribute type." ); |
132 | } |
133 | |
134 | return VET_FLOAT4; |
135 | } |
136 | |
137 | bool GLSLParamParser::attribNameToElementSemantic(const String& name, VertexElementSemantic& semantic, UINT16& index) |
138 | { |
139 | static GLSLAttribute attributes[] = |
140 | { |
141 | GLSLAttribute("bs_position" , VES_POSITION), |
142 | GLSLAttribute("bs_normal" , VES_NORMAL), |
143 | GLSLAttribute("bs_tangent" , VES_TANGENT), |
144 | GLSLAttribute("bs_bitangent" , VES_BITANGENT), |
145 | GLSLAttribute("bs_texcoord" , VES_TEXCOORD), |
146 | GLSLAttribute("bs_color" , VES_COLOR), |
147 | GLSLAttribute("bs_blendweights" , VES_BLEND_WEIGHTS), |
148 | GLSLAttribute("bs_blendindices" , VES_BLEND_INDICES), |
149 | GLSLAttribute("POSITION" , VES_POSITION), |
150 | GLSLAttribute("NORMAL" , VES_NORMAL), |
151 | GLSLAttribute("TANGENT" , VES_TANGENT), |
152 | GLSLAttribute("BITANGENT" , VES_BITANGENT), |
153 | GLSLAttribute("TEXCOORD" , VES_TEXCOORD), |
154 | GLSLAttribute("COLOR" , VES_COLOR), |
155 | GLSLAttribute("BLENDWEIGHT" , VES_BLEND_WEIGHTS), |
156 | GLSLAttribute("BLENDINDICES" , VES_BLEND_INDICES) |
157 | }; |
158 | |
159 | static const UINT32 numAttribs = sizeof(attributes) / sizeof(attributes[0]); |
160 | |
161 | for (UINT32 i = 0; i < numAttribs; i++) |
162 | { |
163 | INT32 attribIndex = attributes[i].matchesName(name); |
164 | if (attribIndex != -1) |
165 | { |
166 | index = attribIndex; |
167 | semantic = attributes[i].getSemantic(); |
168 | return true; |
169 | } |
170 | } |
171 | |
172 | return false; |
173 | } |
174 | |
175 | void GLSLParamParser::buildUniformDescriptions(GLuint glProgram, GpuProgramType type, GpuParamDesc& returnParamDesc) |
176 | { |
177 | // Scan through the active uniform blocks |
178 | GLint maxBufferSize = 0; |
179 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxBufferSize); |
180 | BS_CHECK_GL_ERROR(); |
181 | |
182 | GLint maxBlockNameBufferSize = 0; |
183 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxBlockNameBufferSize); |
184 | BS_CHECK_GL_ERROR(); |
185 | |
186 | GLint maxStorageBlockNameBufferSize = 0; |
187 | |
188 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
189 | glGetProgramInterfaceiv(glProgram, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &maxStorageBlockNameBufferSize); |
190 | BS_CHECK_GL_ERROR(); |
191 | #endif |
192 | |
193 | maxBufferSize = std::max(maxBufferSize, maxBlockNameBufferSize); |
194 | maxBufferSize = std::max(maxBufferSize, maxStorageBlockNameBufferSize); |
195 | |
196 | GLchar* uniformName = (GLchar*)bs_alloc(sizeof(GLchar)* maxBufferSize); |
197 | |
198 | GpuParamBlockDesc newGlobalBlockDesc; |
199 | newGlobalBlockDesc.slot = 0; |
200 | newGlobalBlockDesc.set = mapParameterToSet(type, ParamType::UniformBlock); |
201 | newGlobalBlockDesc.name = "BS_INTERNAL_Globals" ; |
202 | newGlobalBlockDesc.blockSize = 0; |
203 | newGlobalBlockDesc.isShareable = false; |
204 | |
205 | returnParamDesc.paramBlocks[newGlobalBlockDesc.name] = newGlobalBlockDesc; |
206 | GpuParamBlockDesc& globalBlockDesc = returnParamDesc.paramBlocks[newGlobalBlockDesc.name]; |
207 | |
208 | // Enumerate uniform blocks |
209 | GLint uniformBlockCount = 0; |
210 | |
211 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
212 | // Use program interface extension if available |
213 | glGetProgramInterfaceiv(glProgram, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); |
214 | BS_CHECK_GL_ERROR(); |
215 | #else |
216 | // Fall back to old API if not available |
217 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &uniformBlockCount); |
218 | BS_CHECK_GL_ERROR(); |
219 | #endif |
220 | |
221 | Map<UINT32, String> blockSlotToName; |
222 | Set<String> blockNames; |
223 | for (GLuint index = 0; index < (GLuint)uniformBlockCount; index++) |
224 | { |
225 | GLsizei unusedSize = 0; |
226 | |
227 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
228 | glGetProgramResourceName(glProgram, GL_UNIFORM_BLOCK, index, maxBufferSize, &unusedSize, uniformName); |
229 | BS_CHECK_GL_ERROR(); |
230 | #else |
231 | glGetActiveUniformBlockName(glProgram, index, maxBlockNameBufferSize, &unusedSize, uniformName); |
232 | BS_CHECK_GL_ERROR(); |
233 | #endif |
234 | |
235 | GpuParamBlockDesc newBlockDesc; |
236 | newBlockDesc.slot = index + 1; |
237 | newBlockDesc.set = mapParameterToSet(type, ParamType::UniformBlock); |
238 | newBlockDesc.name = uniformName; |
239 | newBlockDesc.blockSize = 0; |
240 | newBlockDesc.isShareable = true; |
241 | |
242 | returnParamDesc.paramBlocks[newBlockDesc.name] = newBlockDesc; |
243 | blockSlotToName.insert(std::make_pair(index + 1, newBlockDesc.name)); |
244 | blockNames.insert(newBlockDesc.name); |
245 | } |
246 | |
247 | #if BS_OPENGL_4_3 || BS_OPENGLES_3_1 |
248 | // Scan through the shared storage blocks |
249 | GLint storageBlockCount = 0; |
250 | glGetProgramInterfaceiv(glProgram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &storageBlockCount); |
251 | BS_CHECK_GL_ERROR(); |
252 | |
253 | for (GLuint index = 0; index < (GLuint)storageBlockCount; index++) |
254 | { |
255 | GLsizei unusedSize = 0; |
256 | glGetProgramResourceName(glProgram, GL_SHADER_STORAGE_BLOCK, index, maxBufferSize, &unusedSize, uniformName); |
257 | BS_CHECK_GL_ERROR(); |
258 | |
259 | GpuParamObjectDesc bufferParam; |
260 | bufferParam.name = uniformName; |
261 | bufferParam.slot = index; |
262 | bufferParam.type = GPOT_RWSTRUCTURED_BUFFER; |
263 | bufferParam.set = mapParameterToSet(type, ParamType::StorageBlock); |
264 | |
265 | returnParamDesc.buffers.insert(std::make_pair(uniformName, bufferParam)); |
266 | } |
267 | #endif |
268 | |
269 | Map<String, UINT32> foundFirstArrayIndex; |
270 | Map<String, GpuParamDataDesc> foundStructs; |
271 | |
272 | // Get the number of active uniforms |
273 | GLint uniformCount = 0; |
274 | glGetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &uniformCount); |
275 | BS_CHECK_GL_ERROR(); |
276 | |
277 | // Loop over each of the active uniforms, and add them to the reference container |
278 | // only do this for user defined uniforms, ignore built in gl state uniforms |
279 | for (GLuint index = 0; index < (GLuint)uniformCount; index++) |
280 | { |
281 | GLsizei arraySize = 0; |
282 | glGetActiveUniformName(glProgram, index, maxBufferSize, &arraySize, uniformName); |
283 | BS_CHECK_GL_ERROR(); |
284 | |
285 | String paramName = String(uniformName); |
286 | |
287 | // Naming rules and packing rules used here are described in |
288 | // OpenGL Core Specification 2.11.4 |
289 | |
290 | // Check if parameter is a part of a struct |
291 | Vector<String> nameElements = StringUtil::tokenise(paramName, "." ); |
292 | |
293 | bool inStruct = false; |
294 | String structName; |
295 | if (nameElements.size() > 1) |
296 | { |
297 | auto uniformBlockFind = blockNames.find(nameElements[0]); |
298 | |
299 | // Check if the name is not a struct, and instead a Uniform block namespace |
300 | if (uniformBlockFind != blockNames.end()) |
301 | { |
302 | // Possibly it's a struct inside a named uniform block |
303 | if (nameElements.size() > 2) |
304 | { |
305 | inStruct = true; |
306 | structName = nameElements[1]; |
307 | paramName = nameElements.back(); |
308 | } |
309 | } |
310 | else |
311 | { |
312 | inStruct = true; |
313 | structName = nameElements[0]; |
314 | paramName = nameElements.back(); |
315 | } |
316 | } |
317 | |
318 | String cleanParamName = paramName; // Param name without array indexes |
319 | |
320 | // Check if the parameter is in an array |
321 | UINT32 arrayIdx = 0; |
322 | bool isInArray = false; |
323 | if (inStruct) |
324 | { |
325 | // If the uniform name has a "[" in it then its an array element uniform. |
326 | String::size_type arrayStart = structName.find("[" ); |
327 | String::size_type arrayEnd = structName.find("]" ); |
328 | if (arrayStart != String::npos) |
329 | { |
330 | String strArrIdx = structName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1)); |
331 | arrayIdx = parseUINT32(strArrIdx, 0); |
332 | isInArray = true; |
333 | |
334 | structName = structName.substr(0, arrayStart); |
335 | } |
336 | } |
337 | |
338 | { |
339 | // If the uniform name has a "[" in it then its an array element uniform. |
340 | String::size_type arrayStart = cleanParamName.find("[" ); |
341 | String::size_type arrayEnd = cleanParamName.find("]" ); |
342 | if (arrayStart != String::npos) |
343 | { |
344 | String strArrIdx = cleanParamName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1)); |
345 | |
346 | // If in struct, we don't care about individual element array indices |
347 | if(!inStruct) |
348 | { |
349 | arrayIdx = parseUINT32(strArrIdx, 0); |
350 | isInArray = true; |
351 | } |
352 | |
353 | cleanParamName = cleanParamName.substr(0, arrayStart); |
354 | } |
355 | } |
356 | |
357 | // GLSL will optimize out unused array indexes, so there's no guarantee that 0 is the first, |
358 | // so we store the first one here |
359 | int firstArrayIndex = 0; |
360 | if (isInArray) |
361 | { |
362 | String nameToSearch = cleanParamName; |
363 | if (inStruct) |
364 | nameToSearch = structName; |
365 | |
366 | auto arrayIndexFind = foundFirstArrayIndex.find(nameToSearch); |
367 | if (arrayIndexFind == foundFirstArrayIndex.end()) |
368 | { |
369 | foundFirstArrayIndex[nameToSearch] = arrayIdx; |
370 | } |
371 | |
372 | firstArrayIndex = foundFirstArrayIndex[nameToSearch]; |
373 | } |
374 | |
375 | |
376 | GLint uniformType; |
377 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_TYPE, &uniformType); |
378 | BS_CHECK_GL_ERROR(); |
379 | |
380 | GpuParamObjectType samplerType = GPOT_UNKNOWN; |
381 | GpuParamObjectType textureType = GPOT_UNKNOWN; |
382 | |
383 | bool isSampler = false; |
384 | bool isImage = false; |
385 | bool isBuffer = false; |
386 | bool isRWBuffer = false; |
387 | switch (uniformType) |
388 | { |
389 | case GL_SAMPLER_1D: |
390 | case GL_SAMPLER_1D_SHADOW: |
391 | case GL_UNSIGNED_INT_SAMPLER_1D: |
392 | case GL_INT_SAMPLER_1D: |
393 | samplerType = GPOT_SAMPLER1D; |
394 | textureType = GPOT_TEXTURE1D; |
395 | isSampler = true; |
396 | break; |
397 | case GL_SAMPLER_1D_ARRAY: |
398 | case GL_SAMPLER_1D_ARRAY_SHADOW: |
399 | case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: |
400 | case GL_INT_SAMPLER_1D_ARRAY: |
401 | samplerType = GPOT_SAMPLER1D; |
402 | textureType = GPOT_TEXTURE1DARRAY; |
403 | isSampler = true; |
404 | break; |
405 | case GL_SAMPLER_2D: |
406 | case GL_SAMPLER_2D_SHADOW: |
407 | case GL_UNSIGNED_INT_SAMPLER_2D: |
408 | case GL_INT_SAMPLER_2D: |
409 | samplerType = GPOT_SAMPLER2D; |
410 | textureType = GPOT_TEXTURE2D; |
411 | isSampler = true; |
412 | break; |
413 | case GL_SAMPLER_2D_ARRAY: |
414 | case GL_SAMPLER_2D_ARRAY_SHADOW: |
415 | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: |
416 | case GL_INT_SAMPLER_2D_ARRAY: |
417 | samplerType = GPOT_SAMPLER2D; |
418 | textureType = GPOT_TEXTURE2DARRAY; |
419 | isSampler = true; |
420 | break; |
421 | case GL_SAMPLER_2D_MULTISAMPLE: |
422 | case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: |
423 | case GL_INT_SAMPLER_2D_MULTISAMPLE: |
424 | samplerType = GPOT_SAMPLER2DMS; |
425 | textureType = GPOT_TEXTURE2DMS; |
426 | isSampler = true; |
427 | break; |
428 | case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: |
429 | case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: |
430 | case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: |
431 | samplerType = GPOT_SAMPLER2DMS; |
432 | textureType = GPOT_TEXTURE2DMSARRAY; |
433 | isSampler = true; |
434 | break; |
435 | case GL_SAMPLER_3D: |
436 | case GL_UNSIGNED_INT_SAMPLER_3D: |
437 | case GL_INT_SAMPLER_3D: |
438 | samplerType = GPOT_SAMPLER3D; |
439 | textureType = GPOT_TEXTURE3D; |
440 | isSampler = true; |
441 | break; |
442 | case GL_SAMPLER_CUBE: |
443 | case GL_SAMPLER_CUBE_SHADOW: |
444 | case GL_UNSIGNED_INT_SAMPLER_CUBE: |
445 | case GL_INT_SAMPLER_CUBE: |
446 | samplerType = GPOT_SAMPLERCUBE; |
447 | textureType = GPOT_TEXTURECUBE; |
448 | isSampler = true; |
449 | break; |
450 | case GL_SAMPLER_CUBE_MAP_ARRAY: |
451 | case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: |
452 | case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: |
453 | case GL_INT_SAMPLER_CUBE_MAP_ARRAY: |
454 | samplerType = GPOT_SAMPLERCUBE; |
455 | textureType = GPOT_TEXTURECUBEARRAY; |
456 | isSampler = true; |
457 | break; |
458 | case GL_SAMPLER_BUFFER: |
459 | case GL_UNSIGNED_INT_SAMPLER_BUFFER: |
460 | case GL_INT_SAMPLER_BUFFER: |
461 | isBuffer = true; |
462 | break; |
463 | #if BS_OPENGL_4_2 || BS_OPENGLES_3_1 |
464 | case GL_IMAGE_1D: |
465 | case GL_UNSIGNED_INT_IMAGE_1D: |
466 | case GL_INT_IMAGE_1D: |
467 | textureType = GPOT_RWTEXTURE1D; |
468 | isImage = true; |
469 | break; |
470 | case GL_IMAGE_1D_ARRAY: |
471 | case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: |
472 | case GL_INT_IMAGE_1D_ARRAY: |
473 | textureType = GPOT_RWTEXTURE1DARRAY; |
474 | isImage = true; |
475 | break; |
476 | case GL_IMAGE_2D: |
477 | case GL_UNSIGNED_INT_IMAGE_2D: |
478 | case GL_INT_IMAGE_2D: |
479 | textureType = GPOT_RWTEXTURE2D; |
480 | isImage = true; |
481 | break; |
482 | case GL_IMAGE_2D_ARRAY: |
483 | case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: |
484 | case GL_INT_IMAGE_2D_ARRAY: |
485 | textureType = GPOT_RWTEXTURE2DARRAY; |
486 | isImage = true; |
487 | break; |
488 | case GL_IMAGE_2D_MULTISAMPLE: |
489 | case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: |
490 | case GL_INT_IMAGE_2D_MULTISAMPLE: |
491 | textureType = GPOT_RWTEXTURE2DMS; |
492 | isImage = true; |
493 | break; |
494 | case GL_IMAGE_2D_MULTISAMPLE_ARRAY: |
495 | case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: |
496 | case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: |
497 | textureType = GPOT_RWTEXTURE2DMSARRAY; |
498 | isImage = true; |
499 | break; |
500 | case GL_IMAGE_3D: |
501 | case GL_UNSIGNED_INT_IMAGE_3D: |
502 | case GL_INT_IMAGE_3D: |
503 | textureType = GPOT_RWTEXTURE3D; |
504 | isImage = true; |
505 | break; |
506 | case GL_IMAGE_BUFFER: |
507 | case GL_UNSIGNED_INT_IMAGE_BUFFER: |
508 | case GL_INT_IMAGE_BUFFER: |
509 | isRWBuffer = true; |
510 | break; |
511 | #endif |
512 | } |
513 | |
514 | if (isSampler) |
515 | { |
516 | GpuParamObjectDesc samplerParam; |
517 | samplerParam.name = paramName; |
518 | samplerParam.type = samplerType; |
519 | samplerParam.slot = glGetUniformLocation(glProgram, uniformName); |
520 | samplerParam.set = mapParameterToSet(type, ParamType::Sampler); |
521 | |
522 | GpuParamObjectDesc textureParam; |
523 | textureParam.name = paramName; |
524 | textureParam.type = textureType; |
525 | textureParam.slot = samplerParam.slot; |
526 | textureParam.set = mapParameterToSet(type, ParamType::Texture); |
527 | |
528 | returnParamDesc.samplers.insert(std::make_pair(paramName, samplerParam)); |
529 | returnParamDesc.textures.insert(std::make_pair(paramName, textureParam)); |
530 | |
531 | BS_CHECK_GL_ERROR(); |
532 | } |
533 | else if (isImage) |
534 | { |
535 | GpuParamObjectDesc textureParam; |
536 | textureParam.name = paramName; |
537 | textureParam.type = textureType; |
538 | textureParam.slot = glGetUniformLocation(glProgram, uniformName); |
539 | textureParam.set = mapParameterToSet(type, ParamType::Image); |
540 | |
541 | returnParamDesc.loadStoreTextures.insert(std::make_pair(paramName, textureParam)); |
542 | |
543 | BS_CHECK_GL_ERROR(); |
544 | } |
545 | else if (isBuffer) |
546 | { |
547 | GpuParamObjectDesc bufferParam; |
548 | bufferParam.name = paramName; |
549 | bufferParam.type = GPOT_BYTE_BUFFER; |
550 | bufferParam.slot = glGetUniformLocation(glProgram, uniformName); |
551 | bufferParam.set = mapParameterToSet(type, ParamType::Texture); |
552 | |
553 | returnParamDesc.buffers.insert(std::make_pair(paramName, bufferParam)); |
554 | |
555 | BS_CHECK_GL_ERROR(); |
556 | } |
557 | else if(isRWBuffer) |
558 | { |
559 | GpuParamObjectDesc bufferParam; |
560 | bufferParam.name = paramName; |
561 | bufferParam.type = GPOT_RWBYTE_BUFFER; |
562 | bufferParam.slot = glGetUniformLocation(glProgram, uniformName); |
563 | bufferParam.set = mapParameterToSet(type, ParamType::Image); |
564 | |
565 | returnParamDesc.buffers.insert(std::make_pair(paramName, bufferParam)); |
566 | |
567 | BS_CHECK_GL_ERROR(); |
568 | } |
569 | else |
570 | { |
571 | // If array index is larger than 0 and uniform is not a part of a struct, |
572 | // it means we already processed it (struct arrays are processed differently) |
573 | if (!inStruct && arrayIdx != 0) |
574 | continue; |
575 | |
576 | GLint blockIndex; |
577 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_BLOCK_INDEX, &blockIndex); |
578 | BS_CHECK_GL_ERROR(); |
579 | |
580 | GpuParamDataDesc gpuParam; |
581 | |
582 | if (isInArray) |
583 | gpuParam.name = cleanParamName; |
584 | else |
585 | gpuParam.name = paramName; |
586 | |
587 | determineParamInfo(gpuParam, paramName, glProgram, index); |
588 | |
589 | if (blockIndex != -1) |
590 | { |
591 | GLint blockOffset; |
592 | glGetActiveUniformsiv(glProgram, 1, &index, GL_UNIFORM_OFFSET, &blockOffset); |
593 | |
594 | blockOffset = blockOffset / 4; |
595 | |
596 | gpuParam.gpuMemOffset = blockOffset; |
597 | |
598 | String& blockName = blockSlotToName[blockIndex + 1]; |
599 | GpuParamBlockDesc& curBlockDesc = returnParamDesc.paramBlocks[blockName]; |
600 | |
601 | gpuParam.paramBlockSlot = curBlockDesc.slot; |
602 | gpuParam.paramBlockSet = mapParameterToSet(type, ParamType::UniformBlock); |
603 | gpuParam.cpuMemOffset = blockOffset; |
604 | curBlockDesc.blockSize = std::max(curBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize); |
605 | |
606 | BS_CHECK_GL_ERROR(); |
607 | } |
608 | else |
609 | { |
610 | gpuParam.gpuMemOffset = glGetUniformLocation(glProgram, uniformName); |
611 | gpuParam.paramBlockSlot = 0; |
612 | gpuParam.paramBlockSet = mapParameterToSet(type, ParamType::UniformBlock); |
613 | gpuParam.cpuMemOffset = globalBlockDesc.blockSize; |
614 | |
615 | globalBlockDesc.blockSize = std::max(globalBlockDesc.blockSize, gpuParam.cpuMemOffset + gpuParam.arrayElementStride * gpuParam.arraySize); |
616 | |
617 | BS_CHECK_GL_ERROR(); |
618 | } |
619 | |
620 | // If parameter is not a part of a struct we're done. Also done if parameter is part of a struct, but |
621 | // not part of a uniform block (in which case we treat struct members as separate parameters) |
622 | if (!inStruct || blockIndex == -1) |
623 | { |
624 | returnParamDesc.params.insert(std::make_pair(gpuParam.name, gpuParam)); |
625 | continue; |
626 | } |
627 | |
628 | // If the parameter is part of a struct, then we need to update the struct definition |
629 | auto findExistingStruct = foundStructs.find(structName); |
630 | |
631 | // Create new definition if one doesn't exist |
632 | if (findExistingStruct == foundStructs.end()) |
633 | { |
634 | foundStructs[structName] = GpuParamDataDesc(); |
635 | GpuParamDataDesc& structDesc = foundStructs[structName]; |
636 | structDesc.type = GPDT_STRUCT; |
637 | structDesc.name = structName; |
638 | structDesc.arraySize = 1; |
639 | structDesc.elementSize = 0; |
640 | structDesc.arrayElementStride = 0; |
641 | structDesc.gpuMemOffset = gpuParam.gpuMemOffset; |
642 | structDesc.cpuMemOffset = gpuParam.cpuMemOffset; |
643 | structDesc.paramBlockSlot = gpuParam.paramBlockSlot; |
644 | structDesc.paramBlockSet = gpuParam.paramBlockSet; |
645 | } |
646 | |
647 | // Update struct with size of the new parameter |
648 | GpuParamDataDesc& structDesc = foundStructs[structName]; |
649 | |
650 | if (arrayIdx == (UINT32)firstArrayIndex) // Determine element size only using the first array element |
651 | { |
652 | structDesc.elementSize = std::max(structDesc.elementSize, gpuParam.cpuMemOffset + |
653 | gpuParam.arrayElementStride * gpuParam.arraySize); |
654 | |
655 | structDesc.gpuMemOffset = std::min(structDesc.gpuMemOffset, gpuParam.gpuMemOffset); |
656 | structDesc.cpuMemOffset = std::min(structDesc.cpuMemOffset, gpuParam.cpuMemOffset); |
657 | } |
658 | |
659 | structDesc.arraySize = std::max(structDesc.arraySize, arrayIdx + 1); |
660 | } |
661 | } |
662 | |
663 | for(auto& entry : foundStructs) |
664 | { |
665 | entry.second.elementSize = entry.second.elementSize - entry.second.cpuMemOffset; |
666 | entry.second.arrayElementStride = Math::divideAndRoundUp(entry.second.elementSize, 4U) * 4; |
667 | |
668 | returnParamDesc.params.insert(std::make_pair(entry.first, entry.second)); |
669 | } |
670 | |
671 | // Param blocks always need to be a multiple of 4, so make it so |
672 | for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter) |
673 | { |
674 | GpuParamBlockDesc& blockDesc = iter->second; |
675 | |
676 | if (blockDesc.blockSize % 4 != 0) |
677 | blockDesc.blockSize += (4 - (blockDesc.blockSize % 4)); |
678 | } |
679 | |
680 | #if BS_DEBUG_MODE |
681 | // Check if manually calculated and OpenGL buffer sizes match |
682 | for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter) |
683 | { |
684 | if (iter->second.slot == 0) |
685 | continue; |
686 | |
687 | GLint blockSize = 0; |
688 | glGetActiveUniformBlockiv(glProgram, iter->second.slot - 1, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); |
689 | BS_CHECK_GL_ERROR(); |
690 | |
691 | assert(blockSize % 4 == 0); |
692 | blockSize = blockSize / 4; |
693 | |
694 | if ((INT32)iter->second.blockSize != blockSize) |
695 | BS_EXCEPT(InternalErrorException, "OpenGL specified and manual uniform block buffer sizes don't match!" ); |
696 | } |
697 | #endif |
698 | |
699 | bs_free(uniformName); |
700 | } |
701 | |
702 | void GLSLParamParser::determineParamInfo(GpuParamDataDesc& desc, const String& paramName, GLuint programHandle, GLuint uniformIndex) |
703 | { |
704 | GLint arraySize; |
705 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_SIZE, &arraySize); |
706 | BS_CHECK_GL_ERROR(); |
707 | desc.arraySize = arraySize; |
708 | |
709 | GLint uniformType; |
710 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_TYPE, &uniformType); |
711 | BS_CHECK_GL_ERROR(); |
712 | |
713 | switch (uniformType) |
714 | { |
715 | case GL_BOOL: |
716 | desc.type = GPDT_BOOL; |
717 | desc.elementSize = 1; |
718 | break; |
719 | case GL_FLOAT: |
720 | desc.type = GPDT_FLOAT1; |
721 | desc.elementSize = 1; |
722 | break; |
723 | case GL_FLOAT_VEC2: |
724 | desc.type = GPDT_FLOAT2; |
725 | desc.elementSize = 2; |
726 | break; |
727 | case GL_FLOAT_VEC3: |
728 | desc.type = GPDT_FLOAT3; |
729 | desc.elementSize = 3; |
730 | break; |
731 | case GL_FLOAT_VEC4: |
732 | desc.type = GPDT_FLOAT4; |
733 | desc.elementSize = 4; |
734 | break; |
735 | case GL_INT: |
736 | case GL_UNSIGNED_INT: |
737 | desc.type = GPDT_INT1; |
738 | desc.elementSize = 1; |
739 | break; |
740 | case GL_INT_VEC2: |
741 | case GL_UNSIGNED_INT_VEC2: |
742 | desc.type = GPDT_INT2; |
743 | desc.elementSize = 2; |
744 | break; |
745 | case GL_INT_VEC3: |
746 | case GL_UNSIGNED_INT_VEC3: |
747 | desc.type = GPDT_INT3; |
748 | desc.elementSize = 3; |
749 | break; |
750 | case GL_INT_VEC4: |
751 | case GL_UNSIGNED_INT_VEC4: |
752 | desc.type = GPDT_INT4; |
753 | desc.elementSize = 4; |
754 | break; |
755 | case GL_FLOAT_MAT2: |
756 | desc.type = GPDT_MATRIX_2X2; |
757 | desc.elementSize = 4; |
758 | break; |
759 | case GL_FLOAT_MAT3: |
760 | desc.type = GPDT_MATRIX_3X3; |
761 | desc.elementSize = 9; |
762 | break; |
763 | case GL_FLOAT_MAT4: |
764 | desc.type = GPDT_MATRIX_4X4; |
765 | desc.elementSize = 16; |
766 | break; |
767 | case GL_FLOAT_MAT2x3: |
768 | desc.type = GPDT_MATRIX_2X3; |
769 | desc.elementSize = 6; |
770 | break; |
771 | case GL_FLOAT_MAT3x2: |
772 | desc.type = GPDT_MATRIX_3X2; |
773 | desc.elementSize = 6; |
774 | break; |
775 | case GL_FLOAT_MAT2x4: |
776 | desc.type = GPDT_MATRIX_2X4; |
777 | desc.elementSize = 8; |
778 | break; |
779 | case GL_FLOAT_MAT4x2: |
780 | desc.type = GPDT_MATRIX_4X2; |
781 | desc.elementSize = 8; |
782 | break; |
783 | case GL_FLOAT_MAT3x4: |
784 | desc.type = GPDT_MATRIX_3X4; |
785 | desc.elementSize = 12; |
786 | break; |
787 | case GL_FLOAT_MAT4x3: |
788 | desc.type = GPDT_MATRIX_4X3; |
789 | desc.elementSize = 12; |
790 | break; |
791 | default: |
792 | BS_EXCEPT(InternalErrorException, "Invalid shader parameter type: " + toString(uniformType) + " for parameter " + paramName); |
793 | } |
794 | |
795 | if (arraySize > 1) |
796 | { |
797 | GLint arrayStride; |
798 | glGetActiveUniformsiv(programHandle, 1, &uniformIndex, GL_UNIFORM_ARRAY_STRIDE, &arrayStride); |
799 | BS_CHECK_GL_ERROR(); |
800 | |
801 | if (arrayStride > 0) |
802 | { |
803 | assert(arrayStride % 4 == 0); |
804 | |
805 | desc.arrayElementStride = arrayStride / 4; |
806 | } |
807 | else |
808 | desc.arrayElementStride = desc.elementSize; |
809 | } |
810 | else |
811 | desc.arrayElementStride = desc.elementSize; |
812 | } |
813 | |
814 | UINT32 GLSLParamParser::mapParameterToSet(GpuProgramType progType, ParamType paramType) |
815 | { |
816 | UINT32 progTypeIdx = (UINT32)progType; |
817 | UINT32 paramTypeIdx = (UINT32)paramType; |
818 | |
819 | return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx; |
820 | } |
821 | }} |
822 | |