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