Merge branch 'devel/master' into devel/graphics
[platform/core/uifw/dali-core.git] / dali / internal / render / shaders / program.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/render/shaders/program.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring>
23 #include <iomanip>
24 #include <map>
25
26 // INTERNAL INCLUDES
27 #include <dali/devel-api/common/hash.h>
28 #include <dali/graphics-api/graphics-controller.h>
29 #include <dali/graphics-api/graphics-program.h>
30 #include <dali/graphics-api/graphics-reflection.h>
31 #include <dali/integration-api/debug.h>
32 #include <dali/integration-api/gl-defines.h>
33 #include <dali/internal/common/shader-data.h>
34 #include <dali/internal/render/common/performance-monitor.h>
35 #include <dali/internal/render/gl-resources/gl-call-debug.h>
36 #include <dali/internal/render/shaders/program-cache.h>
37 #include <dali/public-api/common/constants.h>
38 #include <dali/public-api/common/dali-common.h>
39 #include <dali/public-api/common/dali-vector.h>
40
41 namespace Dali
42 {
43 namespace Internal
44 {
45 // LOCAL STUFF
46 namespace
47 {
48 const char* const gStdAttribs[Program::ATTRIB_TYPE_LAST] =
49   {
50     "aPosition", // ATTRIB_POSITION
51     "aTexCoord", // ATTRIB_TEXCOORD
52 };
53
54 const char* const gStdUniforms[Program::UNIFORM_TYPE_LAST] =
55   {
56     "uMvpMatrix",    // UNIFORM_MVP_MATRIX
57     "uModelView",    // UNIFORM_MODELVIEW_MATRIX
58     "uProjection",   // UNIFORM_PROJECTION_MATRIX
59     "uModelMatrix",  // UNIFORM_MODEL_MATRIX,
60     "uViewMatrix",   // UNIFORM_VIEW_MATRIX,
61     "uNormalMatrix", // UNIFORM_NORMAL_MATRIX
62     "uColor",        // UNIFORM_COLOR
63     "sTexture",      // UNIFORM_SAMPLER
64     "sTextureRect",  // UNIFORM_SAMPLER_RECT
65     "sEffect",       // UNIFORM_EFFECT_SAMPLER
66     "uSize"          // UNIFORM_SIZE
67 };
68
69 const unsigned int NUMBER_OF_DEFAULT_UNIFORMS = static_cast<unsigned int>(Program::DefaultUniformIndex::COUNT);
70
71 /**
72  * List of all default uniforms used for quicker lookup
73  */
74 size_t DEFAULT_UNIFORM_HASHTABLE[NUMBER_OF_DEFAULT_UNIFORMS] =
75   {
76     CalculateHash(std::string("uModelMatrix")),
77     CalculateHash(std::string("uMvpMatrix")),
78     CalculateHash(std::string("uViewMatrix")),
79     CalculateHash(std::string("uModelView")),
80     CalculateHash(std::string("uNormalMatrix")),
81     CalculateHash(std::string("uProjection")),
82     CalculateHash(std::string("uSize")),
83     CalculateHash(std::string("uColor"))};
84
85 } // namespace
86
87 // IMPLEMENTATION
88
89 Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& gfxController, Graphics::UniquePtr<Graphics::Program>&& gfxProgram, bool modifiesGeometry)
90 {
91   uint32_t programId{0u};
92
93   // Get program id and use it as hash for the cache
94   // in order to maintain current functionality as long as needed
95   gfxController.GetProgramParameter(*gfxProgram, 1, &programId);
96
97   size_t shaderHash = programId;
98
99   Program* program = cache.GetProgram(shaderHash);
100
101   if(nullptr == program)
102   {
103     // program not found so create it
104     program = new Program(cache, shaderData, gfxController, std::move(gfxProgram), modifiesGeometry);
105     program->GetActiveSamplerUniforms();
106     cache.AddProgram(shaderHash, program);
107   }
108
109   return program;
110 }
111
112 void Program::Use()
113 {
114   LOG_GL("UseProgram(%d)\n", mProgramId);
115   CHECK_GL(mGlAbstraction, mGlAbstraction.UseProgram(mProgramId));
116   mCache.SetCurrentProgram(this);
117 }
118
119 bool Program::IsUsed()
120 {
121   return (this == mCache.GetCurrentProgram());
122 }
123
124 GLint Program::GetAttribLocation(AttribType type)
125 {
126   DALI_ASSERT_DEBUG(type != ATTRIB_UNKNOWN);
127
128   return GetCustomAttributeLocation(type);
129 }
130
131 uint32_t Program::RegisterCustomAttribute(ConstString name)
132 {
133   uint32_t index = 0;
134   // find the value from cache
135   for(; index < static_cast<uint32_t>(mAttributeLocations.size()); ++index)
136   {
137     if(mAttributeLocations[index].first == name)
138     {
139       // name found so return index
140       return index;
141     }
142   }
143   // if we get here, index is one past end so push back the new name
144   mAttributeLocations.push_back(std::make_pair(name, ATTRIB_UNKNOWN));
145   return index;
146 }
147
148 GLint Program::GetCustomAttributeLocation(uint32_t attributeIndex)
149 {
150   // debug check that index is within name cache
151   DALI_ASSERT_DEBUG(mAttributeLocations.size() > attributeIndex);
152
153   // check if we have already queried the location of the attribute
154   GLint location = mAttributeLocations[attributeIndex].second;
155
156   if(location == ATTRIB_UNKNOWN)
157   {
158     location = CHECK_GL(mGlAbstraction, mGlAbstraction.GetAttribLocation(mProgramId, mAttributeLocations[attributeIndex].first.GetCString()));
159
160     mAttributeLocations[attributeIndex].second = location;
161     LOG_GL("GetAttributeLocation(program=%d,%s) = %d\n", mProgramId, mAttributeLocations[attributeIndex].first.GetCString(), mAttributeLocations[attributeIndex].second);
162   }
163
164   return location;
165 }
166
167 uint32_t Program::RegisterUniform(ConstString name)
168 {
169   uint32_t index = 0;
170   // find the value from cache
171   for(; index < static_cast<uint32_t>(mUniformLocations.size()); ++index)
172   {
173     if(mUniformLocations[index].first == name)
174     {
175       // name found so return index
176       return index;
177     }
178   }
179   // if we get here, index is one past end so push back the new name
180   mUniformLocations.push_back(std::make_pair(name, UNIFORM_NOT_QUERIED));
181   return index;
182 }
183
184 GLint Program::GetUniformLocation(uint32_t uniformIndex)
185 {
186   // debug check that index is within name cache
187   DALI_ASSERT_DEBUG(mUniformLocations.size() > uniformIndex);
188
189   // check if we have already queried the location of the uniform
190   GLint location = mUniformLocations[uniformIndex].second;
191
192   if(location == UNIFORM_NOT_QUERIED)
193   {
194     location = CHECK_GL(mGlAbstraction, mGlAbstraction.GetUniformLocation(mProgramId, mUniformLocations[uniformIndex].first.GetCString()));
195
196     mUniformLocations[uniformIndex].second = location;
197     LOG_GL("GetUniformLocation(program=%d,%s) = %d\n", mProgramId, mUniformLocations[uniformIndex].first.GetCString(), mUniformLocations[uniformIndex].second);
198   }
199
200   return location;
201 }
202
203 namespace
204 {
205 /**
206  * This struct is used to record the position of a uniform declaration
207  * within the fragment shader source code.
208  */
209 struct LocationPosition
210 {
211   GLint   uniformLocation; ///< The location of the uniform (used as an identifier)
212   int32_t position;        ///< the position of the uniform declaration
213   LocationPosition(GLint uniformLocation, int32_t position)
214   : uniformLocation(uniformLocation),
215     position(position)
216   {
217   }
218 };
219
220 bool sortByPosition(LocationPosition a, LocationPosition b)
221 {
222   return a.position < b.position;
223 }
224
225 const char* const DELIMITERS = " \t\n";
226
227 struct StringSize
228 {
229   const char* const mString;
230   const uint32_t    mLength;
231
232   template<uint32_t kLength>
233   constexpr StringSize(const char (&string)[kLength])
234   : mString(string),
235     mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
236   {
237   }
238
239   operator const char*() const
240   {
241     return mString;
242   }
243 };
244
245 bool operator==(const StringSize& lhs, const char* rhs)
246 {
247   return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
248 }
249
250 constexpr StringSize UNIFORM{"uniform"};
251 constexpr StringSize SAMPLER_PREFIX{"sampler"};
252 constexpr StringSize SAMPLER_TYPES[] = {
253   "2D",
254   "Cube",
255   "ExternalOES"};
256
257 constexpr auto END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
258
259 } // namespace
260
261 void Program::GetActiveSamplerUniforms()
262 {
263   GLint numberOfActiveUniforms = -1;
264   GLint uniformMaxNameLength   = -1;
265
266   mGlAbstraction.GetProgramiv(mProgramId, GL_ACTIVE_UNIFORMS, &numberOfActiveUniforms);
267   mGlAbstraction.GetProgramiv(mProgramId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxNameLength);
268
269   std::vector<std::string>      samplerNames;
270   std::vector<char>             name(uniformMaxNameLength + 1); // Allow for null terminator
271   std::vector<LocationPosition> samplerUniformLocations;
272
273   {
274     int    nameLength = -1;
275     int    number     = -1;
276     GLenum type       = GL_ZERO;
277
278     for(int i = 0; i < numberOfActiveUniforms; ++i)
279     {
280       mGlAbstraction.GetActiveUniform(mProgramId, static_cast<GLuint>(i), uniformMaxNameLength, &nameLength, &number, &type, name.data());
281
282       if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES)
283       {
284         GLuint location = mGlAbstraction.GetUniformLocation(mProgramId, name.data());
285         samplerNames.push_back(name.data());
286         samplerUniformLocations.push_back(LocationPosition(location, -1));
287       }
288     }
289   }
290
291   //Determine declaration order of each sampler
292   char* fragShader      = strdup(mProgramData->GetFragmentShader());
293   char* uniform         = strstr(fragShader, UNIFORM);
294   int   samplerPosition = 0;
295   while(uniform)
296   {
297     char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
298
299     char* nextPtr = nullptr;
300     char* token   = strtok_r(outerToken, DELIMITERS, &nextPtr);
301     while(token)
302     {
303       if(SAMPLER_PREFIX == token)
304       {
305         token += SAMPLER_PREFIX.mLength;
306         if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
307         {
308           bool found(false);
309           token = strtok_r(nullptr, DELIMITERS, &nextPtr);
310           for(uint32_t i = 0; i < static_cast<uint32_t>(samplerUniformLocations.size()); ++i)
311           {
312             if(samplerUniformLocations[i].position == -1 &&
313                strncmp(token, samplerNames[i].c_str(), samplerNames[i].size()) == 0)
314             {
315               samplerUniformLocations[i].position = samplerPosition++;
316               found                               = true;
317               break;
318             }
319           }
320
321           if(!found)
322           {
323             DALI_LOG_ERROR("Sampler uniform %s declared but not used in the shader\n", token);
324           }
325           break;
326         }
327       }
328
329       token = strtok_r(nullptr, DELIMITERS, &nextPtr);
330     }
331
332     uniform = strstr(uniform, UNIFORM);
333   }
334
335   free(fragShader);
336
337   // Re-order according to declaration order in the fragment source.
338   uint32_t samplerUniformCount = static_cast<uint32_t>(samplerUniformLocations.size());
339   if(samplerUniformCount > 1)
340   {
341     std::sort(samplerUniformLocations.begin(), samplerUniformLocations.end(), sortByPosition);
342   }
343
344   mSamplerUniformLocations.resize(samplerUniformCount);
345   for(uint32_t i = 0; i < samplerUniformCount; ++i)
346   {
347     mSamplerUniformLocations[i] = samplerUniformLocations[i].uniformLocation;
348   }
349 }
350
351 bool Program::GetSamplerUniformLocation(uint32_t index, GLint& location)
352 {
353   bool result = false;
354   if(index < mSamplerUniformLocations.size())
355   {
356     location = mSamplerUniformLocations[index];
357     result   = true;
358   }
359   return result;
360 }
361
362 uint32_t Program::GetActiveSamplerCount() const
363 {
364   return static_cast<uint32_t>(mSamplerUniformLocations.size());
365 }
366
367 void Program::SetUniform1i(GLint location, GLint value0)
368 {
369   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
370
371   if(UNIFORM_UNKNOWN == location)
372   {
373     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
374     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
375     // specified uniform variable will not be changed.following opengl silently do nothing
376     return;
377   }
378
379   // check if uniform location fits the cache
380   if(location >= MAX_UNIFORM_CACHE_SIZE)
381   {
382     // not cached, make the gl call
383     LOG_GL("Uniform1i(%d,%d)\n", location, value0);
384     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1i(location, value0));
385   }
386   else
387   {
388     // check if the value is different from what's already been set
389     if(value0 != mUniformCacheInt[location])
390     {
391       // make the gl call
392       LOG_GL("Uniform1i(%d,%d)\n", location, value0);
393       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1i(location, value0));
394       // update cache
395       mUniformCacheInt[location] = value0;
396     }
397   }
398 }
399
400 void Program::SetUniform4i(GLint location, GLint value0, GLint value1, GLint value2, GLint value3)
401 {
402   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
403
404   if(UNIFORM_UNKNOWN == location)
405   {
406     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
407     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
408     // specified uniform variable will not be changed.following opengl silently do nothing
409     return;
410   }
411
412   // Not caching these as based on current analysis this is not called that often by our shaders
413   LOG_GL("Uniform4i(%d,%d,%d,%d,%d)\n", location, value0, value1, value2, value3);
414   CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform4i(location, value0, value1, value2, value3));
415 }
416
417 void Program::SetUniform1f(GLint location, GLfloat value0)
418 {
419   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
420
421   if(UNIFORM_UNKNOWN == location)
422   {
423     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
424     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
425     // specified uniform variable will not be changed.following opengl silently do nothing
426     return;
427   }
428
429   // check if uniform location fits the cache
430   if(location >= MAX_UNIFORM_CACHE_SIZE)
431   {
432     // not cached, make the gl call
433     LOG_GL("Uniform1f(%d,%f)\n", location, value0);
434     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1f(location, value0));
435   }
436   else
437   {
438     // check if the same value has already been set, reset if it is different
439     if((fabsf(value0 - mUniformCacheFloat[location]) >= Math::MACHINE_EPSILON_1))
440     {
441       // make the gl call
442       LOG_GL("Uniform1f(%d,%f)\n", location, value0);
443       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1f(location, value0));
444
445       // update cache
446       mUniformCacheFloat[location] = value0;
447     }
448   }
449 }
450
451 void Program::SetUniform2f(GLint location, GLfloat value0, GLfloat value1)
452 {
453   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
454
455   if(UNIFORM_UNKNOWN == location)
456   {
457     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
458     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
459     // specified uniform variable will not be changed.following opengl silently do nothing
460     return;
461   }
462
463   // check if uniform location fits the cache
464   if(location >= MAX_UNIFORM_CACHE_SIZE)
465   {
466     // not cached, make the gl call
467     LOG_GL("Uniform2f(%d,%f,%f)\n", location, value0, value1);
468     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform2f(location, value0, value1));
469   }
470   else
471   {
472     // check if the same value has already been set, reset if it is different
473     if((fabsf(value0 - mUniformCacheFloat2[location][0]) >= Math::MACHINE_EPSILON_1) ||
474        (fabsf(value1 - mUniformCacheFloat2[location][1]) >= Math::MACHINE_EPSILON_1))
475     {
476       // make the gl call
477       LOG_GL("Uniform2f(%d,%f,%f)\n", location, value0, value1);
478       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform2f(location, value0, value1));
479
480       // update cache
481       mUniformCacheFloat2[location][0] = value0;
482       mUniformCacheFloat2[location][1] = value1;
483     }
484   }
485 }
486
487 void Program::SetSizeUniform3f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2)
488 {
489   if((fabsf(value0 - mSizeUniformCache.x) >= Math::MACHINE_EPSILON_1) ||
490      (fabsf(value1 - mSizeUniformCache.y) >= Math::MACHINE_EPSILON_1) ||
491      (fabsf(value2 - mSizeUniformCache.z) >= Math::MACHINE_EPSILON_1))
492   {
493     mSizeUniformCache.x = value0;
494     mSizeUniformCache.y = value1;
495     mSizeUniformCache.z = value2;
496     SetUniform3f(location, value0, value1, value2);
497   }
498 }
499
500 void Program::SetUniform3f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2)
501 {
502   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
503
504   if(UNIFORM_UNKNOWN == location)
505   {
506     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
507     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
508     // specified uniform variable will not be changed.following opengl silently do nothing
509     return;
510   }
511
512   // Not caching these as based on current analysis this is not called that often by our shaders
513   LOG_GL("Uniform3f(%d,%f,%f,%f)\n", location, value0, value1, value2);
514   CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform3f(location, value0, value1, value2));
515 }
516
517 void Program::SetUniform4f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3)
518 {
519   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
520
521   if(UNIFORM_UNKNOWN == location)
522   {
523     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
524     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
525     // specified uniform variable will not be changed.following opengl silently do nothing
526     return;
527   }
528
529   // check if uniform location fits the cache
530   if(location >= MAX_UNIFORM_CACHE_SIZE)
531   {
532     // not cached, make the gl call
533     LOG_GL("Uniform4f(%d,%f,%f,%f,%f)\n", location, value0, value1, value2, value3);
534     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform4f(location, value0, value1, value2, value3));
535   }
536   else
537   {
538     // check if the same value has already been set, reset if any component is different
539     // checking index 3 first because we're often animating alpha (rgba)
540     if((fabsf(value3 - mUniformCacheFloat4[location][3]) >= Math::MACHINE_EPSILON_1) ||
541        (fabsf(value0 - mUniformCacheFloat4[location][0]) >= Math::MACHINE_EPSILON_1) ||
542        (fabsf(value1 - mUniformCacheFloat4[location][1]) >= Math::MACHINE_EPSILON_1) ||
543        (fabsf(value2 - mUniformCacheFloat4[location][2]) >= Math::MACHINE_EPSILON_1))
544     {
545       // make the gl call
546       LOG_GL("Uniform4f(%d,%f,%f,%f,%f)\n", location, value0, value1, value2, value3);
547       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform4f(location, value0, value1, value2, value3));
548       // update cache
549       mUniformCacheFloat4[location][0] = value0;
550       mUniformCacheFloat4[location][1] = value1;
551       mUniformCacheFloat4[location][2] = value2;
552       mUniformCacheFloat4[location][3] = value3;
553     }
554   }
555 }
556
557 void Program::SetUniformMatrix4fv(GLint location, GLsizei count, const GLfloat* value)
558 {
559   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
560
561   if(UNIFORM_UNKNOWN == location)
562   {
563     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
564     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
565     // specified uniform variable will not be changed.following opengl silently do nothing
566     return;
567   }
568
569   // Not caching these calls. Based on current analysis this is called very often
570   // but with different values (we're using this for MVP matrices)
571   // NOTE! we never want driver or GPU to transpose
572   LOG_GL("UniformMatrix4fv(%d,%d,GL_FALSE,%x)\n", location, count, value);
573   CHECK_GL(mGlAbstraction, mGlAbstraction.UniformMatrix4fv(location, count, GL_FALSE, value));
574 }
575
576 void Program::SetUniformMatrix3fv(GLint location, GLsizei count, const GLfloat* value)
577 {
578   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
579
580   if(UNIFORM_UNKNOWN == location)
581   {
582     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
583     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
584     // specified uniform variable will not be changed.following opengl silently do nothing
585     return;
586   }
587
588   // Not caching these calls. Based on current analysis this is called very often
589   // but with different values (we're using this for MVP matrices)
590   // NOTE! we never want driver or GPU to transpose
591   LOG_GL("UniformMatrix3fv(%d,%d,GL_FALSE,%x)\n", location, count, value);
592   CHECK_GL(mGlAbstraction, mGlAbstraction.UniformMatrix3fv(location, count, GL_FALSE, value));
593 }
594
595 void Program::GlContextCreated()
596 {
597 }
598
599 void Program::GlContextDestroyed()
600 {
601   ResetAttribsUniformCache();
602 }
603
604 bool Program::ModifiesGeometry()
605 {
606   return mModifiesGeometry;
607 }
608
609 Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphics::Controller& controller, Graphics::UniquePtr<Graphics::Program>&& gfxProgram, bool modifiesGeometry)
610 : mCache(cache),
611   mGlAbstraction(mCache.GetGlAbstraction()),
612   mProjectionMatrix(nullptr),
613   mViewMatrix(nullptr),
614   mProgramId(0),
615   mGfxProgram(std::move(gfxProgram)),
616   mGfxController(controller),
617   mProgramData(shaderData),
618   mModifiesGeometry(modifiesGeometry)
619 {
620   // reserve space for standard attributes
621   mAttributeLocations.reserve(ATTRIB_TYPE_LAST);
622   for(uint32_t i = 0; i < ATTRIB_TYPE_LAST; ++i)
623   {
624     RegisterCustomAttribute(ConstString(gStdAttribs[i]));
625   }
626
627   // reserve space for standard uniforms
628   mUniformLocations.reserve(UNIFORM_TYPE_LAST);
629   // reset built in uniform names in cache
630   for(uint32_t i = 0; i < UNIFORM_TYPE_LAST; ++i)
631   {
632     RegisterUniform(ConstString(gStdUniforms[i]));
633   }
634
635   // reset values
636   ResetAttribsUniformCache();
637
638   // Get program id and use it as hash for the cache
639   // in order to maintain current functionality as long as needed
640   mGfxController.GetProgramParameter(*mGfxProgram, 1, &mProgramId);
641
642   BuildReflection(controller.GetProgramReflection(*mGfxProgram.get()));
643 }
644
645 Program::~Program()
646 {
647 }
648
649 void Program::ResetAttribsUniformCache()
650 {
651   // reset attribute locations
652   for(uint32_t i = 0; i < mAttributeLocations.size(); ++i)
653   {
654     mAttributeLocations[i].second = ATTRIB_UNKNOWN;
655   }
656
657   // reset all gl uniform locations
658   for(uint32_t i = 0; i < mUniformLocations.size(); ++i)
659   {
660     // reset gl program locations and names
661     mUniformLocations[i].second = UNIFORM_NOT_QUERIED;
662   }
663
664   mSamplerUniformLocations.clear();
665
666   // reset uniform caches
667   mSizeUniformCache.x = mSizeUniformCache.y = mSizeUniformCache.z = 0.f;
668
669   for(uint32_t i = 0; i < MAX_UNIFORM_CACHE_SIZE; ++i)
670   {
671     // GL initializes uniforms to 0
672     mUniformCacheInt[i]       = 0;
673     mUniformCacheFloat[i]     = 0.0f;
674     mUniformCacheFloat2[i][0] = 0.0f;
675     mUniformCacheFloat2[i][1] = 0.0f;
676     mUniformCacheFloat4[i][0] = 0.0f;
677     mUniformCacheFloat4[i][1] = 0.0f;
678     mUniformCacheFloat4[i][2] = 0.0f;
679     mUniformCacheFloat4[i][3] = 0.0f;
680   }
681 }
682
683 void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
684 {
685   mReflectionDefaultUniforms.clear();
686   mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
687
688   auto uniformBlockCount = graphicsReflection.GetUniformBlockCount();
689
690   // add uniform block fields
691   for(auto i = 0u; i < uniformBlockCount; ++i)
692   {
693     Graphics::UniformBlockInfo uboInfo;
694     graphicsReflection.GetUniformBlock(i, uboInfo);
695
696     // for each member store data
697     for(const auto& item : uboInfo.members)
698     {
699       auto hashValue = CalculateHash(item.name);
700       mReflection.emplace_back(ReflectionUniformInfo{hashValue, false, item});
701
702       // update buffer index
703       mReflection.back().uniformInfo.bufferIndex = i;
704
705       // Update default uniforms
706       for(auto i = 0u; i < NUMBER_OF_DEFAULT_UNIFORMS; ++i)
707       {
708         if(hashValue == DEFAULT_UNIFORM_HASHTABLE[i])
709         {
710           mReflectionDefaultUniforms[i] = mReflection.back();
711           break;
712         }
713       }
714     }
715   }
716
717   // add samplers
718   auto samplers = graphicsReflection.GetSamplers();
719   for(const auto& sampler : samplers)
720   {
721     mReflection.emplace_back(ReflectionUniformInfo{CalculateHash(sampler.name), false, sampler});
722   }
723
724   // check for potential collisions
725   std::map<size_t, bool> hashTest;
726   bool                   hasCollisions(false);
727   for(auto&& item : mReflection)
728   {
729     if(hashTest.find(item.hashValue) == hashTest.end())
730     {
731       hashTest[item.hashValue] = false;
732     }
733     else
734     {
735       hashTest[item.hashValue] = true;
736       hasCollisions            = true;
737     }
738   }
739
740   // update collision flag for further use
741   if(hasCollisions)
742   {
743     for(auto&& item : mReflection)
744     {
745       item.hasCollision = hashTest[item.hashValue];
746     }
747   }
748 }
749
750 bool Program::GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const
751 {
752   if(mReflection.empty())
753   {
754     return false;
755   }
756
757   hashedName = !hashedName ? CalculateHash(name, '[') : hashedName;
758
759   for(const ReflectionUniformInfo& item : mReflection)
760   {
761     if(item.hashValue == hashedName)
762     {
763       if(!item.hasCollision || item.uniformInfo.name == name)
764       {
765         out = item.uniformInfo;
766         return true;
767       }
768       else
769       {
770         return false;
771       }
772     }
773   }
774   return false;
775 }
776
777 const Graphics::UniformInfo* Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const
778 {
779   if(mReflectionDefaultUniforms.empty())
780   {
781     return nullptr;
782   }
783
784   const auto value = &mReflectionDefaultUniforms[static_cast<uint32_t>(defaultUniformIndex)];
785   return &value->uniformInfo;
786 }
787
788 } // namespace Internal
789
790 } // namespace Dali