Updated all code to new format
[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
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/gl-defines.h>
28 #include <dali/internal/common/shader-data.h>
29 #include <dali/internal/render/common/performance-monitor.h>
30 #include <dali/internal/render/gl-resources/gl-call-debug.h>
31 #include <dali/internal/render/shaders/program-cache.h>
32 #include <dali/public-api/common/constants.h>
33 #include <dali/public-api/common/dali-common.h>
34 #include <dali/public-api/common/dali-vector.h>
35
36 namespace
37 {
38 void LogWithLineNumbers(const char* source)
39 {
40   uint32_t    lineNumber = 0u;
41   const char* prev       = source;
42   const char* ptr        = prev;
43
44   while(true)
45   {
46     if(lineNumber > 200u)
47     {
48       break;
49     }
50     // seek the next end of line or end of text
51     while(*ptr != '\n' && *ptr != '\0')
52     {
53       ++ptr;
54     }
55
56     std::string line(prev, ptr - prev);
57     Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugError, "%4d %s\n", lineNumber, line.c_str());
58
59     if(*ptr == '\0')
60     {
61       break;
62     }
63     prev = ++ptr;
64     ++lineNumber;
65   }
66 }
67
68 } //namespace
69
70 namespace Dali
71 {
72 namespace Internal
73 {
74 // LOCAL STUFF
75 namespace
76 {
77 const char* const gStdAttribs[Program::ATTRIB_TYPE_LAST] =
78   {
79     "aPosition", // ATTRIB_POSITION
80     "aTexCoord", // ATTRIB_TEXCOORD
81 };
82
83 const char* const gStdUniforms[Program::UNIFORM_TYPE_LAST] =
84   {
85     "uMvpMatrix",    // UNIFORM_MVP_MATRIX
86     "uModelView",    // UNIFORM_MODELVIEW_MATRIX
87     "uProjection",   // UNIFORM_PROJECTION_MATRIX
88     "uModelMatrix",  // UNIFORM_MODEL_MATRIX,
89     "uViewMatrix",   // UNIFORM_VIEW_MATRIX,
90     "uNormalMatrix", // UNIFORM_NORMAL_MATRIX
91     "uColor",        // UNIFORM_COLOR
92     "sTexture",      // UNIFORM_SAMPLER
93     "sTextureRect",  // UNIFORM_SAMPLER_RECT
94     "sEffect",       // UNIFORM_EFFECT_SAMPLER
95     "uSize"          // UNIFORM_SIZE
96 };
97
98 } // namespace
99
100 // IMPLEMENTATION
101
102 Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, bool modifiesGeometry)
103 {
104   size_t   shaderHash = shaderData->GetHashValue();
105   Program* program    = cache.GetProgram(shaderHash);
106
107   if(nullptr == program)
108   {
109     // program not found so create it
110     program = new Program(cache, shaderData, modifiesGeometry);
111     program->Load();
112     cache.AddProgram(shaderHash, program);
113   }
114
115   return program;
116 }
117
118 void Program::Use()
119 {
120   if(mLinked)
121   {
122     if(this != mCache.GetCurrentProgram())
123     {
124       LOG_GL("UseProgram(%d)\n", mProgramId);
125       CHECK_GL(mGlAbstraction, mGlAbstraction.UseProgram(mProgramId));
126
127       mCache.SetCurrentProgram(this);
128     }
129   }
130 }
131
132 bool Program::IsUsed()
133 {
134   return (this == mCache.GetCurrentProgram());
135 }
136
137 GLint Program::GetAttribLocation(AttribType type)
138 {
139   DALI_ASSERT_DEBUG(type != ATTRIB_UNKNOWN);
140
141   return GetCustomAttributeLocation(type);
142 }
143
144 uint32_t Program::RegisterCustomAttribute(ConstString name)
145 {
146   uint32_t index = 0;
147   // find the value from cache
148   for(; index < static_cast<uint32_t>(mAttributeLocations.size()); ++index)
149   {
150     if(mAttributeLocations[index].first == name)
151     {
152       // name found so return index
153       return index;
154     }
155   }
156   // if we get here, index is one past end so push back the new name
157   mAttributeLocations.push_back(std::make_pair(name, ATTRIB_UNKNOWN));
158   return index;
159 }
160
161 GLint Program::GetCustomAttributeLocation(uint32_t attributeIndex)
162 {
163   // debug check that index is within name cache
164   DALI_ASSERT_DEBUG(mAttributeLocations.size() > attributeIndex);
165
166   // check if we have already queried the location of the attribute
167   GLint location = mAttributeLocations[attributeIndex].second;
168
169   if(location == ATTRIB_UNKNOWN)
170   {
171     location = CHECK_GL(mGlAbstraction, mGlAbstraction.GetAttribLocation(mProgramId, mAttributeLocations[attributeIndex].first.GetCString()));
172
173     mAttributeLocations[attributeIndex].second = location;
174     LOG_GL("GetAttributeLocation(program=%d,%s) = %d\n", mProgramId, mAttributeLocations[attributeIndex].first.GetCString(), mAttributeLocations[attributeIndex].second);
175   }
176
177   return location;
178 }
179
180 uint32_t Program::RegisterUniform(ConstString name)
181 {
182   uint32_t index = 0;
183   // find the value from cache
184   for(; index < static_cast<uint32_t>(mUniformLocations.size()); ++index)
185   {
186     if(mUniformLocations[index].first == name)
187     {
188       // name found so return index
189       return index;
190     }
191   }
192   // if we get here, index is one past end so push back the new name
193   mUniformLocations.push_back(std::make_pair(name, UNIFORM_NOT_QUERIED));
194   return index;
195 }
196
197 GLint Program::GetUniformLocation(uint32_t uniformIndex)
198 {
199   // debug check that index is within name cache
200   DALI_ASSERT_DEBUG(mUniformLocations.size() > uniformIndex);
201
202   // check if we have already queried the location of the uniform
203   GLint location = mUniformLocations[uniformIndex].second;
204
205   if(location == UNIFORM_NOT_QUERIED)
206   {
207     location = CHECK_GL(mGlAbstraction, mGlAbstraction.GetUniformLocation(mProgramId, mUniformLocations[uniformIndex].first.GetCString()));
208
209     mUniformLocations[uniformIndex].second = location;
210     LOG_GL("GetUniformLocation(program=%d,%s) = %d\n", mProgramId, mUniformLocations[uniformIndex].first.GetCString(), mUniformLocations[uniformIndex].second);
211   }
212
213   return location;
214 }
215
216 namespace
217 {
218 /**
219  * This struct is used to record the position of a uniform declaration
220  * within the fragment shader source code.
221  */
222 struct LocationPosition
223 {
224   GLint   uniformLocation; ///< The location of the uniform (used as an identifier)
225   int32_t position;        ///< the position of the uniform declaration
226   LocationPosition(GLint uniformLocation, int32_t position)
227   : uniformLocation(uniformLocation),
228     position(position)
229   {
230   }
231 };
232
233 bool sortByPosition(LocationPosition a, LocationPosition b)
234 {
235   return a.position < b.position;
236 }
237
238 const char* const DELIMITERS = " \t\n";
239
240 struct StringSize
241 {
242   const char* const mString;
243   const uint32_t    mLength;
244
245   template<uint32_t kLength>
246   constexpr StringSize(const char (&string)[kLength])
247   : mString(string),
248     mLength(kLength - 1) // remove terminating null; N.B. there should be no other null.
249   {
250   }
251
252   operator const char*() const
253   {
254     return mString;
255   }
256 };
257
258 bool operator==(const StringSize& lhs, const char* rhs)
259 {
260   return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
261 }
262
263 constexpr StringSize UNIFORM{"uniform"};
264 constexpr StringSize SAMPLER_PREFIX{"sampler"};
265 constexpr StringSize SAMPLER_TYPES[] = {
266   "2D",
267   "Cube",
268   "ExternalOES"};
269
270 constexpr auto END_SAMPLER_TYPES = SAMPLER_TYPES + std::extent<decltype(SAMPLER_TYPES)>::value;
271
272 } // namespace
273
274 void Program::GetActiveSamplerUniforms()
275 {
276   GLint numberOfActiveUniforms = -1;
277   GLint uniformMaxNameLength   = -1;
278
279   mGlAbstraction.GetProgramiv(mProgramId, GL_ACTIVE_UNIFORMS, &numberOfActiveUniforms);
280   mGlAbstraction.GetProgramiv(mProgramId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxNameLength);
281
282   std::vector<std::string>      samplerNames;
283   std::vector<char>             name(uniformMaxNameLength + 1); // Allow for null terminator
284   std::vector<LocationPosition> samplerUniformLocations;
285
286   {
287     int    nameLength = -1;
288     int    number     = -1;
289     GLenum type       = GL_ZERO;
290
291     for(int i = 0; i < numberOfActiveUniforms; ++i)
292     {
293       mGlAbstraction.GetActiveUniform(mProgramId, static_cast<GLuint>(i), uniformMaxNameLength, &nameLength, &number, &type, name.data());
294
295       if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES)
296       {
297         GLuint location = mGlAbstraction.GetUniformLocation(mProgramId, name.data());
298         samplerNames.push_back(name.data());
299         samplerUniformLocations.push_back(LocationPosition(location, -1));
300       }
301     }
302   }
303
304   //Determine declaration order of each sampler
305   char* fragShader      = strdup(mProgramData->GetFragmentShader());
306   char* uniform         = strstr(fragShader, UNIFORM);
307   int   samplerPosition = 0;
308   while(uniform)
309   {
310     char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
311
312     char* nextPtr = nullptr;
313     char* token   = strtok_r(outerToken, DELIMITERS, &nextPtr);
314     while(token)
315     {
316       if(SAMPLER_PREFIX == token)
317       {
318         token += SAMPLER_PREFIX.mLength;
319         if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
320         {
321           bool found(false);
322           token = strtok_r(nullptr, DELIMITERS, &nextPtr);
323           for(uint32_t i = 0; i < static_cast<uint32_t>(samplerUniformLocations.size()); ++i)
324           {
325             if(samplerUniformLocations[i].position == -1 &&
326                strncmp(token, samplerNames[i].c_str(), samplerNames[i].size()) == 0)
327             {
328               samplerUniformLocations[i].position = samplerPosition++;
329               found                               = true;
330               break;
331             }
332           }
333
334           if(!found)
335           {
336             DALI_LOG_ERROR("Sampler uniform %s declared but not used in the shader\n", token);
337           }
338           break;
339         }
340       }
341
342       token = strtok_r(nullptr, DELIMITERS, &nextPtr);
343     }
344
345     uniform = strstr(uniform, UNIFORM);
346   }
347
348   free(fragShader);
349
350   // Re-order according to declaration order in the fragment source.
351   uint32_t samplerUniformCount = static_cast<uint32_t>(samplerUniformLocations.size());
352   if(samplerUniformCount > 1)
353   {
354     std::sort(samplerUniformLocations.begin(), samplerUniformLocations.end(), sortByPosition);
355   }
356
357   mSamplerUniformLocations.resize(samplerUniformCount);
358   for(uint32_t i = 0; i < samplerUniformCount; ++i)
359   {
360     mSamplerUniformLocations[i] = samplerUniformLocations[i].uniformLocation;
361   }
362 }
363
364 bool Program::GetSamplerUniformLocation(uint32_t index, GLint& location)
365 {
366   bool result = false;
367   if(index < mSamplerUniformLocations.size())
368   {
369     location = mSamplerUniformLocations[index];
370     result   = true;
371   }
372   return result;
373 }
374
375 uint32_t Program::GetActiveSamplerCount() const
376 {
377   return static_cast<uint32_t>(mSamplerUniformLocations.size());
378 }
379
380 void Program::SetUniform1i(GLint location, GLint value0)
381 {
382   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
383
384   if(UNIFORM_UNKNOWN == location)
385   {
386     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
387     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
388     // specified uniform variable will not be changed.following opengl silently do nothing
389     return;
390   }
391
392   // check if uniform location fits the cache
393   if(location >= MAX_UNIFORM_CACHE_SIZE)
394   {
395     // not cached, make the gl call
396     LOG_GL("Uniform1i(%d,%d)\n", location, value0);
397     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1i(location, value0));
398   }
399   else
400   {
401     // check if the value is different from what's already been set
402     if(value0 != mUniformCacheInt[location])
403     {
404       // make the gl call
405       LOG_GL("Uniform1i(%d,%d)\n", location, value0);
406       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1i(location, value0));
407       // update cache
408       mUniformCacheInt[location] = value0;
409     }
410   }
411 }
412
413 void Program::SetUniform4i(GLint location, GLint value0, GLint value1, GLint value2, GLint value3)
414 {
415   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
416
417   if(UNIFORM_UNKNOWN == location)
418   {
419     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
420     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
421     // specified uniform variable will not be changed.following opengl silently do nothing
422     return;
423   }
424
425   // Not caching these as based on current analysis this is not called that often by our shaders
426   LOG_GL("Uniform4i(%d,%d,%d,%d,%d)\n", location, value0, value1, value2, value3);
427   CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform4i(location, value0, value1, value2, value3));
428 }
429
430 void Program::SetUniform1f(GLint location, GLfloat value0)
431 {
432   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
433
434   if(UNIFORM_UNKNOWN == location)
435   {
436     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
437     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
438     // specified uniform variable will not be changed.following opengl silently do nothing
439     return;
440   }
441
442   // check if uniform location fits the cache
443   if(location >= MAX_UNIFORM_CACHE_SIZE)
444   {
445     // not cached, make the gl call
446     LOG_GL("Uniform1f(%d,%f)\n", location, value0);
447     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1f(location, value0));
448   }
449   else
450   {
451     // check if the same value has already been set, reset if it is different
452     if((fabsf(value0 - mUniformCacheFloat[location]) >= Math::MACHINE_EPSILON_1))
453     {
454       // make the gl call
455       LOG_GL("Uniform1f(%d,%f)\n", location, value0);
456       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform1f(location, value0));
457
458       // update cache
459       mUniformCacheFloat[location] = value0;
460     }
461   }
462 }
463
464 void Program::SetUniform2f(GLint location, GLfloat value0, GLfloat value1)
465 {
466   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
467
468   if(UNIFORM_UNKNOWN == location)
469   {
470     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
471     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
472     // specified uniform variable will not be changed.following opengl silently do nothing
473     return;
474   }
475
476   // check if uniform location fits the cache
477   if(location >= MAX_UNIFORM_CACHE_SIZE)
478   {
479     // not cached, make the gl call
480     LOG_GL("Uniform2f(%d,%f,%f)\n", location, value0, value1);
481     CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform2f(location, value0, value1));
482   }
483   else
484   {
485     // check if the same value has already been set, reset if it is different
486     if((fabsf(value0 - mUniformCacheFloat2[location][0]) >= Math::MACHINE_EPSILON_1) ||
487        (fabsf(value1 - mUniformCacheFloat2[location][1]) >= Math::MACHINE_EPSILON_1))
488     {
489       // make the gl call
490       LOG_GL("Uniform2f(%d,%f,%f)\n", location, value0, value1);
491       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform2f(location, value0, value1));
492
493       // update cache
494       mUniformCacheFloat2[location][0] = value0;
495       mUniformCacheFloat2[location][1] = value1;
496     }
497   }
498 }
499
500 void Program::SetSizeUniform3f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2)
501 {
502   if((fabsf(value0 - mSizeUniformCache.x) >= Math::MACHINE_EPSILON_1) ||
503      (fabsf(value1 - mSizeUniformCache.y) >= Math::MACHINE_EPSILON_1) ||
504      (fabsf(value2 - mSizeUniformCache.z) >= Math::MACHINE_EPSILON_1))
505   {
506     mSizeUniformCache.x = value0;
507     mSizeUniformCache.y = value1;
508     mSizeUniformCache.z = value2;
509     SetUniform3f(location, value0, value1, value2);
510   }
511 }
512
513 void Program::SetUniform3f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2)
514 {
515   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
516
517   if(UNIFORM_UNKNOWN == location)
518   {
519     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
520     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
521     // specified uniform variable will not be changed.following opengl silently do nothing
522     return;
523   }
524
525   // Not caching these as based on current analysis this is not called that often by our shaders
526   LOG_GL("Uniform3f(%d,%f,%f,%f)\n", location, value0, value1, value2);
527   CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform3f(location, value0, value1, value2));
528 }
529
530 void Program::SetUniform4f(GLint location, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat value3)
531 {
532   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
533
534   if(UNIFORM_UNKNOWN == location)
535   {
536     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
537     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
538     // specified uniform variable will not be changed.following opengl silently do nothing
539     return;
540   }
541
542   // check if uniform location fits the cache
543   if(location >= MAX_UNIFORM_CACHE_SIZE)
544   {
545     // not cached, 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   }
549   else
550   {
551     // check if the same value has already been set, reset if any component is different
552     // checking index 3 first because we're often animating alpha (rgba)
553     if((fabsf(value3 - mUniformCacheFloat4[location][3]) >= Math::MACHINE_EPSILON_1) ||
554        (fabsf(value0 - mUniformCacheFloat4[location][0]) >= Math::MACHINE_EPSILON_1) ||
555        (fabsf(value1 - mUniformCacheFloat4[location][1]) >= Math::MACHINE_EPSILON_1) ||
556        (fabsf(value2 - mUniformCacheFloat4[location][2]) >= Math::MACHINE_EPSILON_1))
557     {
558       // make the gl call
559       LOG_GL("Uniform4f(%d,%f,%f,%f,%f)\n", location, value0, value1, value2, value3);
560       CHECK_GL(mGlAbstraction, mGlAbstraction.Uniform4f(location, value0, value1, value2, value3));
561       // update cache
562       mUniformCacheFloat4[location][0] = value0;
563       mUniformCacheFloat4[location][1] = value1;
564       mUniformCacheFloat4[location][2] = value2;
565       mUniformCacheFloat4[location][3] = value3;
566     }
567   }
568 }
569
570 void Program::SetUniformMatrix4fv(GLint location, GLsizei count, const GLfloat* value)
571 {
572   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
573
574   if(UNIFORM_UNKNOWN == location)
575   {
576     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
577     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
578     // specified uniform variable will not be changed.following opengl silently do nothing
579     return;
580   }
581
582   // Not caching these calls. Based on current analysis this is called very often
583   // but with different values (we're using this for MVP matrices)
584   // NOTE! we never want driver or GPU to transpose
585   LOG_GL("UniformMatrix4fv(%d,%d,GL_FALSE,%x)\n", location, count, value);
586   CHECK_GL(mGlAbstraction, mGlAbstraction.UniformMatrix4fv(location, count, GL_FALSE, value));
587 }
588
589 void Program::SetUniformMatrix3fv(GLint location, GLsizei count, const GLfloat* value)
590 {
591   DALI_ASSERT_DEBUG(IsUsed()); // should not call this if this program is not used
592
593   if(UNIFORM_UNKNOWN == location)
594   {
595     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
596     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
597     // specified uniform variable will not be changed.following opengl silently do nothing
598     return;
599   }
600
601   // Not caching these calls. Based on current analysis this is called very often
602   // but with different values (we're using this for MVP matrices)
603   // NOTE! we never want driver or GPU to transpose
604   LOG_GL("UniformMatrix3fv(%d,%d,GL_FALSE,%x)\n", location, count, value);
605   CHECK_GL(mGlAbstraction, mGlAbstraction.UniformMatrix3fv(location, count, GL_FALSE, value));
606 }
607
608 void Program::GlContextCreated()
609 {
610 }
611
612 void Program::GlContextDestroyed()
613 {
614   mLinked           = false;
615   mVertexShaderId   = 0;
616   mFragmentShaderId = 0;
617   mProgramId        = 0;
618
619   ResetAttribsUniformCache();
620 }
621
622 bool Program::ModifiesGeometry()
623 {
624   return mModifiesGeometry;
625 }
626
627 Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, bool modifiesGeometry)
628 : mCache(cache),
629   mGlAbstraction(mCache.GetGlAbstraction()),
630   mProjectionMatrix(nullptr),
631   mViewMatrix(nullptr),
632   mLinked(false),
633   mVertexShaderId(0),
634   mFragmentShaderId(0),
635   mProgramId(0),
636   mProgramData(shaderData),
637   mModifiesGeometry(modifiesGeometry)
638 {
639   // reserve space for standard attributes
640   mAttributeLocations.reserve(ATTRIB_TYPE_LAST);
641   for(uint32_t i = 0; i < ATTRIB_TYPE_LAST; ++i)
642   {
643     RegisterCustomAttribute(ConstString(gStdAttribs[i]));
644   }
645
646   // reserve space for standard uniforms
647   mUniformLocations.reserve(UNIFORM_TYPE_LAST);
648   // reset built in uniform names in cache
649   for(uint32_t i = 0; i < UNIFORM_TYPE_LAST; ++i)
650   {
651     RegisterUniform(ConstString(gStdUniforms[i]));
652   }
653
654   // reset values
655   ResetAttribsUniformCache();
656 }
657
658 Program::~Program()
659 {
660   Unload();
661 }
662
663 void Program::Load()
664 {
665   DALI_ASSERT_ALWAYS(nullptr != mProgramData.Get() && "Program data is not initialized");
666   DALI_ASSERT_DEBUG(mProgramId == 0 && "mProgramId != 0, so about to leak a GL resource by overwriting it.");
667
668   LOG_GL("CreateProgram()\n");
669   mProgramId = CHECK_GL(mGlAbstraction, mGlAbstraction.CreateProgram());
670
671   GLint linked = GL_FALSE;
672
673   const bool binariesSupported = mCache.IsBinarySupported();
674
675   // if shader binaries are supported and ShaderData contains compiled bytecode?
676   if(binariesSupported && mProgramData->HasBinary())
677   {
678     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - Using Compiled Shader, Size = %d\n", mProgramData->GetBufferSize());
679
680     CHECK_GL(mGlAbstraction, mGlAbstraction.ProgramBinary(mProgramId, mCache.ProgramBinaryFormat(), mProgramData->GetBufferData(), static_cast<GLsizei>(mProgramData->GetBufferSize()))); // truncated
681
682     CHECK_GL(mGlAbstraction, mGlAbstraction.ValidateProgram(mProgramId));
683
684     GLint success;
685     CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_VALIDATE_STATUS, &success));
686
687     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ValidateProgram Status = %d\n", success);
688
689     CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_LINK_STATUS, &linked));
690
691     if(GL_FALSE == linked)
692     {
693       DALI_LOG_ERROR("Failed to load program binary \n");
694
695       GLint nLength;
696       CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &nLength));
697       if(nLength > 0)
698       {
699         Dali::Vector<char> szLog;
700         szLog.Reserve(nLength); // Don't call Resize as we don't want to initialise the data, just reserve a buffer
701         CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramInfoLog(mProgramId, nLength, &nLength, szLog.Begin()));
702         DALI_LOG_ERROR("Program Link Error: %s\n", szLog.Begin());
703       }
704     }
705     else
706     {
707       mLinked = true;
708       DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Reused binary.\n");
709     }
710   }
711
712   // Fall back to compiling and linking the vertex and fragment sources
713   if(GL_FALSE == linked)
714   {
715     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - Runtime compilation\n");
716     if(CompileShader(GL_VERTEX_SHADER, mVertexShaderId, mProgramData->GetVertexShader()))
717     {
718       if(CompileShader(GL_FRAGMENT_SHADER, mFragmentShaderId, mProgramData->GetFragmentShader()))
719       {
720         Link();
721
722         if(binariesSupported && mLinked)
723         {
724           GLint  binaryLength = 0;
725           GLenum binaryFormat;
726           DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Compiled and linked.\n\nVS:\n%s\nFS:\n%s\n", mProgramData->GetVertexShader(), mProgramData->GetFragmentShader());
727
728           CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength));
729           DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - GL_PROGRAM_BINARY_LENGTH_OES: %d\n", binaryLength);
730           if(binaryLength > 0)
731           {
732             // Allocate space for the bytecode in ShaderData
733             mProgramData->AllocateBuffer(binaryLength);
734             // Copy the bytecode to ShaderData
735             CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramBinary(mProgramId, binaryLength, nullptr, &binaryFormat, mProgramData->GetBufferData()));
736             mCache.StoreBinary(mProgramData);
737             DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Saved binary.\n");
738           }
739         }
740       }
741     }
742   }
743
744   GetActiveSamplerUniforms();
745
746   // No longer needed
747   FreeShaders();
748 }
749
750 void Program::Unload()
751 {
752   FreeShaders();
753
754   if(this == mCache.GetCurrentProgram())
755   {
756     CHECK_GL(mGlAbstraction, mGlAbstraction.UseProgram(0));
757
758     mCache.SetCurrentProgram(nullptr);
759   }
760
761   if(mProgramId)
762   {
763     LOG_GL("DeleteProgram(%d)\n", mProgramId);
764     CHECK_GL(mGlAbstraction, mGlAbstraction.DeleteProgram(mProgramId));
765     mProgramId = 0;
766   }
767
768   mLinked = false;
769 }
770
771 bool Program::CompileShader(GLenum shaderType, GLuint& shaderId, const char* src)
772 {
773   if(!shaderId)
774   {
775     LOG_GL("CreateShader(%d)\n", shaderType);
776     shaderId = CHECK_GL(mGlAbstraction, mGlAbstraction.CreateShader(shaderType));
777     LOG_GL("AttachShader(%d,%d)\n", mProgramId, shaderId);
778     CHECK_GL(mGlAbstraction, mGlAbstraction.AttachShader(mProgramId, shaderId));
779   }
780
781   LOG_GL("ShaderSource(%d)\n", shaderId);
782   CHECK_GL(mGlAbstraction, mGlAbstraction.ShaderSource(shaderId, 1, &src, nullptr));
783
784   LOG_GL("CompileShader(%d)\n", shaderId);
785   CHECK_GL(mGlAbstraction, mGlAbstraction.CompileShader(shaderId));
786
787   GLint compiled;
788   LOG_GL("GetShaderiv(%d)\n", shaderId);
789   CHECK_GL(mGlAbstraction, mGlAbstraction.GetShaderiv(shaderId, GL_COMPILE_STATUS, &compiled));
790
791   if(compiled == GL_FALSE)
792   {
793     DALI_LOG_ERROR("Failed to compile shader\n");
794     LogWithLineNumbers(src);
795
796     GLint nLength;
797     mGlAbstraction.GetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &nLength);
798     if(nLength > 0)
799     {
800       Dali::Vector<char> szLog;
801       szLog.Reserve(nLength); // Don't call Resize as we don't want to initialise the data, just reserve a buffer
802       mGlAbstraction.GetShaderInfoLog(shaderId, nLength, &nLength, szLog.Begin());
803       DALI_LOG_ERROR("Shader Compiler Error: %s\n", szLog.Begin());
804     }
805
806     DALI_ASSERT_ALWAYS(0 && "Shader compilation failure");
807   }
808
809   return compiled != 0;
810 }
811
812 void Program::Link()
813 {
814   LOG_GL("LinkProgram(%d)\n", mProgramId);
815   CHECK_GL(mGlAbstraction, mGlAbstraction.LinkProgram(mProgramId));
816
817   GLint linked;
818   LOG_GL("GetProgramiv(%d)\n", mProgramId);
819   CHECK_GL(mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_LINK_STATUS, &linked));
820
821   if(linked == GL_FALSE)
822   {
823     DALI_LOG_ERROR("Shader failed to link \n");
824
825     GLint nLength;
826     mGlAbstraction.GetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &nLength);
827     if(nLength > 0)
828     {
829       Dali::Vector<char> szLog;
830       szLog.Reserve(nLength); // Don't call Resize as we don't want to initialise the data, just reserve a buffer
831       mGlAbstraction.GetProgramInfoLog(mProgramId, nLength, &nLength, szLog.Begin());
832       DALI_LOG_ERROR("Shader Link Error: %s\n", szLog.Begin());
833     }
834
835     DALI_ASSERT_ALWAYS(0 && "Shader linking failure");
836   }
837
838   mLinked = linked != GL_FALSE;
839 }
840
841 void Program::FreeShaders()
842 {
843   if(mVertexShaderId)
844   {
845     LOG_GL("DeleteShader(%d)\n", mVertexShaderId);
846     CHECK_GL(mGlAbstraction, mGlAbstraction.DetachShader(mProgramId, mVertexShaderId));
847     CHECK_GL(mGlAbstraction, mGlAbstraction.DeleteShader(mVertexShaderId));
848     mVertexShaderId = 0;
849   }
850
851   if(mFragmentShaderId)
852   {
853     LOG_GL("DeleteShader(%d)\n", mFragmentShaderId);
854     CHECK_GL(mGlAbstraction, mGlAbstraction.DetachShader(mProgramId, mFragmentShaderId));
855     CHECK_GL(mGlAbstraction, mGlAbstraction.DeleteShader(mFragmentShaderId));
856     mFragmentShaderId = 0;
857   }
858 }
859
860 void Program::ResetAttribsUniformCache()
861 {
862   // reset attribute locations
863   for(uint32_t i = 0; i < mAttributeLocations.size(); ++i)
864   {
865     mAttributeLocations[i].second = ATTRIB_UNKNOWN;
866   }
867
868   // reset all gl uniform locations
869   for(uint32_t i = 0; i < mUniformLocations.size(); ++i)
870   {
871     // reset gl program locations and names
872     mUniformLocations[i].second = UNIFORM_NOT_QUERIED;
873   }
874
875   mSamplerUniformLocations.clear();
876
877   // reset uniform caches
878   mSizeUniformCache.x = mSizeUniformCache.y = mSizeUniformCache.z = 0.f;
879
880   for(uint32_t i = 0; i < MAX_UNIFORM_CACHE_SIZE; ++i)
881   {
882     // GL initializes uniforms to 0
883     mUniformCacheInt[i]       = 0;
884     mUniformCacheFloat[i]     = 0.0f;
885     mUniformCacheFloat2[i][0] = 0.0f;
886     mUniformCacheFloat2[i][1] = 0.0f;
887     mUniformCacheFloat4[i][0] = 0.0f;
888     mUniformCacheFloat4[i][1] = 0.0f;
889     mUniformCacheFloat4[i][2] = 0.0f;
890     mUniformCacheFloat4[i][3] = 0.0f;
891   }
892 }
893
894 } // namespace Internal
895
896 } // namespace Dali