Add UniformNameCache to keep track of unique uniform ids to avoid calculating hash...
[platform/core/uifw/dali-core.git] / dali / internal / render / shaders / program.cpp
1 /*
2  * Copyright (c) 2014 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 <iomanip>
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/common/dali-common.h>
26 #include <dali/public-api/common/dali-vector.h>
27 #include <dali/public-api/common/constants.h>
28 #include <dali/integration-api/debug.h>
29 #include <dali/internal/common/shader-data.h>
30 #include <dali/integration-api/gl-defines.h>
31 #include <dali/internal/render/common/performance-monitor.h>
32 #include <dali/internal/render/shaders/program-cache.h>
33 #include <dali/internal/render/gl-resources/gl-call-debug.h>
34
35 namespace
36 {
37 void LogWithLineNumbers( const char * source )
38 {
39   unsigned int lineNumber = 0u;
40   const char *prev = source;
41   const char *ptr = prev;
42
43   while( true )
44   {
45     if(lineNumber > 200u)
46     {
47       break;
48     }
49     // seek the next end of line or end of text
50     while( *ptr!='\n' && *ptr != '\0' )
51     {
52       ++ptr;
53     }
54
55     std::string line( prev, ptr-prev );
56     Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugError, "%4d %s\n", lineNumber, line.c_str());
57
58     if( *ptr == '\0' )
59     {
60       break;
61     }
62     prev = ++ptr;
63     ++lineNumber;
64   }
65 }
66
67 } //namespace
68
69 namespace Dali
70 {
71
72 namespace Internal
73 {
74
75 // LOCAL STUFF
76 namespace
77 {
78
79 const char* gStdAttribs[ Program::ATTRIB_TYPE_LAST ] =
80 {
81   "aPosition",    // ATTRIB_POSITION
82   "aTexCoord",    // ATTRIB_TEXCOORD
83 };
84
85 const char* gStdUniforms[ Program::UNIFORM_TYPE_LAST ] =
86 {
87   "uMvpMatrix",           // UNIFORM_MVP_MATRIX
88   "uModelView",           // UNIFORM_MODELVIEW_MATRIX
89   "uProjection",          // UNIFORM_PROJECTION_MATRIX
90   "uModelMatrix",         // UNIFORM_MODEL_MATRIX,
91   "uViewMatrix",          // UNIFORM_VIEW_MATRIX,
92   "uNormalMatrix",        // UNIFORM_NORMAL_MATRIX
93   "uColor",               // UNIFORM_COLOR
94   "sTexture",             // UNIFORM_SAMPLER
95   "sTextureRect",         // UNIFORM_SAMPLER_RECT
96   "sEffect",              // UNIFORM_EFFECT_SAMPLER
97   "uSize"                 // UNIFORM_SIZE
98 };
99
100 }  // <unnamed> namespace
101
102 // IMPLEMENTATION
103
104 Program* Program::New( ProgramCache& cache, Internal::ShaderDataPtr shaderData, bool modifiesGeometry )
105 {
106   size_t shaderHash = shaderData->GetHashValue();
107   Program* program = cache.GetProgram( shaderHash );
108
109   if( NULL == program )
110   {
111     // program not found so create it
112     program = new Program( cache, shaderData, modifiesGeometry );
113
114     // we want to lazy load programs so dont do a Load yet, it gets done in Use()
115     cache.AddProgram( shaderHash, program );
116   }
117
118   return program;
119 }
120
121 void Program::Use()
122 {
123   if ( !mLinked )
124   {
125     Load();
126   }
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 unsigned int Program::RegisterCustomAttribute( const std::string& name )
153 {
154   unsigned int index = 0;
155   // find the value from cache
156   for( ;index < 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( unsigned int 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.c_str() ) );
180
181     mAttributeLocations[ attributeIndex ].second = location;
182     LOG_GL( "GetAttributeLocation(program=%d,%s) = %d\n", mProgramId, mAttributeLocations[ attributeIndex ].first.c_str(), mAttributeLocations[ attributeIndex ].second );
183   }
184
185   return location;
186 }
187
188
189 unsigned int Program::RegisterUniform( const std::string& name )
190 {
191   unsigned int index = 0;
192   // find the value from cache
193   for( ;index < mUniformLocations.size(); ++index )
194   {
195     if( mUniformLocations[ index ].first == name )
196     {
197       // name found so return index
198       return index;
199     }
200   }
201   // if we get here, index is one past end so push back the new name
202   mUniformLocations.push_back( std::make_pair( name, UNIFORM_NOT_QUERIED ) );
203   return index;
204 }
205
206 GLint Program::GetUniformLocation( unsigned int uniformIndex )
207 {
208   // debug check that index is within name cache
209   DALI_ASSERT_DEBUG( mUniformLocations.size() > uniformIndex );
210
211   // check if we have already queried the location of the uniform
212   GLint location = mUniformLocations[ uniformIndex ].second;
213
214   if( location == UNIFORM_NOT_QUERIED )
215   {
216     location = CHECK_GL( mGlAbstraction, mGlAbstraction.GetUniformLocation( mProgramId, mUniformLocations[ uniformIndex ].first.c_str() ) );
217
218     mUniformLocations[ uniformIndex ].second = location;
219     LOG_GL( "GetUniformLocation(program=%d,%s) = %d\n", mProgramId, mUniformLocations[ uniformIndex ].first.c_str(), mUniformLocations[ uniformIndex ].second );
220   }
221
222   return location;
223 }
224
225 GLint Program::GetSamplerUniformLocation( int32_t uniqueIndex, const std::string& samplerName  )
226 {
227   // don't accept negative values (should never happen)
228   DALI_ASSERT_DEBUG( 0 <= uniqueIndex );
229   const uint32_t index( uniqueIndex ); // avoid compiler warning of signed vs unsigned comparisons
230
231   GLint location = UNIFORM_NOT_QUERIED;
232
233   if( index < mSamplerUniformLocations.Size() )
234   {
235     location = mSamplerUniformLocations[ index ];
236   }
237   else
238   {
239     // not in cache yet, make space and initialize value to not queried
240     mSamplerUniformLocations.Resize( index + 1, UNIFORM_NOT_QUERIED );
241   }
242   if( location == UNIFORM_NOT_QUERIED )
243   {
244     location = CHECK_GL( mGlAbstraction, mGlAbstraction.GetUniformLocation( mProgramId, samplerName.c_str() ) );
245     mSamplerUniformLocations[ index ] = location;
246   }
247   return location;
248 }
249
250 void Program::SetUniform1i( GLint location, GLint value0 )
251 {
252   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
253
254   if( UNIFORM_UNKNOWN == location )
255   {
256     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
257     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
258     // specified uniform variable will not be changed.following opengl silently do nothing
259     return;
260   }
261
262   // check if uniform location fits the cache
263   if( location >= MAX_UNIFORM_CACHE_SIZE )
264   {
265     // not cached, make the gl call
266     LOG_GL( "Uniform1i(%d,%d)\n", location, value0 );
267     CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform1i( location, value0 ) );
268   }
269   else
270   {
271     // check if the value is different from what's already been set
272     if( value0 != mUniformCacheInt[ location ] )
273     {
274       // make the gl call
275       LOG_GL( "Uniform1i(%d,%d)\n", location, value0 );
276       CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform1i( location, value0 ) );
277       // update cache
278       mUniformCacheInt[ location ] = value0;
279     }
280   }
281 }
282
283 void Program::SetUniform4i( GLint location, GLint value0, GLint value1, GLint value2, GLint value3 )
284 {
285   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
286
287   if( UNIFORM_UNKNOWN == location )
288   {
289     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
290     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
291     // specified uniform variable will not be changed.following opengl silently do nothing
292     return;
293   }
294
295   // Not caching these as based on current analysis this is not called that often by our shaders
296   LOG_GL( "Uniform4i(%d,%d,%d,%d,%d)\n", location, value0, value1, value2, value3 );
297   CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform4i( location, value0, value1, value2, value3 ) );
298 }
299
300 void Program::SetUniform1f( GLint location, GLfloat value0 )
301 {
302   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
303
304   if( UNIFORM_UNKNOWN == location )
305   {
306     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
307     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
308     // specified uniform variable will not be changed.following opengl silently do nothing
309     return;
310   }
311
312   // check if uniform location fits the cache
313   if( location >= MAX_UNIFORM_CACHE_SIZE )
314   {
315     // not cached, make the gl call
316     LOG_GL( "Uniform1f(%d,%f)\n", location, value0 );
317     CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform1f( location, value0 ) );
318   }
319   else
320   {
321     // check if the same value has already been set, reset if it is different
322     if( ( fabsf(value0 - mUniformCacheFloat[ location ]) >= Math::MACHINE_EPSILON_1 ) )
323     {
324       // make the gl call
325       LOG_GL( "Uniform1f(%d,%f)\n", location, value0 );
326       CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform1f( location, value0 ) );
327
328       // update cache
329       mUniformCacheFloat[ location ] = value0;
330     }
331   }
332 }
333
334 void Program::SetUniform2f( GLint location, GLfloat value0, GLfloat value1 )
335 {
336   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
337
338   if( UNIFORM_UNKNOWN == location )
339   {
340     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
341     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
342     // specified uniform variable will not be changed.following opengl silently do nothing
343     return;
344   }
345
346   // check if uniform location fits the cache
347   if( location >= MAX_UNIFORM_CACHE_SIZE )
348   {
349     // not cached, make the gl call
350     LOG_GL( "Uniform2f(%d,%f,%f)\n", location, value0, value1 );
351     CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform2f( location, value0, value1 ) );
352   }
353   else
354   {
355     // check if the same value has already been set, reset if it is different
356     if( ( fabsf(value0 - mUniformCacheFloat2[ location ][ 0 ]) >= Math::MACHINE_EPSILON_1 )||
357         ( fabsf(value1 - mUniformCacheFloat2[ location ][ 1 ]) >= Math::MACHINE_EPSILON_1 ) )
358     {
359       // make the gl call
360       LOG_GL( "Uniform2f(%d,%f,%f)\n", location, value0, value1 );
361       CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform2f( location, value0, value1 ) );
362
363       // update cache
364       mUniformCacheFloat2[ location ][ 0 ] = value0;
365       mUniformCacheFloat2[ location ][ 1 ] = value1;
366     }
367   }
368 }
369
370 void Program::SetSizeUniform3f( GLint location, GLfloat value0, GLfloat value1, GLfloat value2 )
371 {
372   if( ( fabsf(value0 - mSizeUniformCache.x) >= Math::MACHINE_EPSILON_1 )||
373       ( fabsf(value1 - mSizeUniformCache.y) >= Math::MACHINE_EPSILON_1 )||
374       ( fabsf(value2 - mSizeUniformCache.z) >= Math::MACHINE_EPSILON_1 ) )
375   {
376     mSizeUniformCache.x = value0;
377     mSizeUniformCache.y = value1;
378     mSizeUniformCache.z = value2;
379     SetUniform3f( location, value0, value1, value2 );
380   }
381 }
382
383 void Program::SetUniform3f( GLint location, GLfloat value0, GLfloat value1, GLfloat value2 )
384 {
385   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
386
387   if( UNIFORM_UNKNOWN == location )
388   {
389     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
390     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
391     // specified uniform variable will not be changed.following opengl silently do nothing
392     return;
393   }
394
395   // Not caching these as based on current analysis this is not called that often by our shaders
396   LOG_GL( "Uniform3f(%d,%f,%f,%f)\n", location, value0, value1, value2 );
397   CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform3f( location, value0, value1, value2 ) );
398 }
399
400 void Program::SetUniform4f( GLint location, GLfloat value0, GLfloat value1, GLfloat value2, GLfloat 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   // check if uniform location fits the cache
413   if( location >= MAX_UNIFORM_CACHE_SIZE )
414   {
415     // not cached, make the gl call
416     LOG_GL( "Uniform4f(%d,%f,%f,%f,%f)\n", location, value0, value1, value2, value3 );
417     CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform4f( location, value0, value1, value2, value3 ) );
418   }
419   else
420   {
421     // check if the same value has already been set, reset if any component is different
422     // checking index 3 first because we're often animating alpha (rgba)
423     if( ( fabsf(value3 - mUniformCacheFloat4[ location ][ 3 ]) >= Math::MACHINE_EPSILON_1 )||
424         ( fabsf(value0 - mUniformCacheFloat4[ location ][ 0 ]) >= Math::MACHINE_EPSILON_1 )||
425         ( fabsf(value1 - mUniformCacheFloat4[ location ][ 1 ]) >= Math::MACHINE_EPSILON_1 )||
426         ( fabsf(value2 - mUniformCacheFloat4[ location ][ 2 ]) >= Math::MACHINE_EPSILON_1 ) )
427     {
428       // make the gl call
429       LOG_GL( "Uniform4f(%d,%f,%f,%f,%f)\n", location, value0, value1, value2, value3 );
430       CHECK_GL( mGlAbstraction, mGlAbstraction.Uniform4f( location, value0, value1, value2, value3 ) );
431       // update cache
432       mUniformCacheFloat4[ location ][ 0 ] = value0;
433       mUniformCacheFloat4[ location ][ 1 ] = value1;
434       mUniformCacheFloat4[ location ][ 2 ] = value2;
435       mUniformCacheFloat4[ location ][ 3 ] = value3;
436     }
437   }
438 }
439
440 void Program::SetUniformMatrix4fv( GLint location, GLsizei count, const GLfloat* value )
441 {
442   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
443
444   if( UNIFORM_UNKNOWN == location )
445   {
446     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
447     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
448     // specified uniform variable will not be changed.following opengl silently do nothing
449     return;
450   }
451
452   // Not caching these calls. Based on current analysis this is called very often
453   // but with different values (we're using this for MVP matrices)
454   // NOTE! we never want driver or GPU to transpose
455   LOG_GL( "UniformMatrix4fv(%d,%d,GL_FALSE,%x)\n", location, count, value );
456   CHECK_GL( mGlAbstraction, mGlAbstraction.UniformMatrix4fv( location, count, GL_FALSE, value ) );
457 }
458
459 void Program::SetUniformMatrix3fv( GLint location, GLsizei count, const GLfloat* value )
460 {
461   DALI_ASSERT_DEBUG( IsUsed() ); // should not call this if this program is not used
462
463   if( UNIFORM_UNKNOWN == location )
464   {
465     // From http://www.khronos.org/opengles/sdk/docs/man/xhtml/glUniform.xml : Notes
466     // If location is equal to UNIFORM_UNKNOWN, the data passed in will be silently ignored and the
467     // specified uniform variable will not be changed.following opengl silently do nothing
468     return;
469   }
470
471
472   // Not caching these calls. Based on current analysis this is called very often
473   // but with different values (we're using this for MVP matrices)
474   // NOTE! we never want driver or GPU to transpose
475   LOG_GL( "UniformMatrix3fv(%d,%d,GL_FALSE,%x)\n", location, count, value );
476   CHECK_GL( mGlAbstraction, mGlAbstraction.UniformMatrix3fv( location, count, GL_FALSE, value ) );
477 }
478
479 void Program::GlContextCreated()
480 {
481 }
482
483 void Program::GlContextDestroyed()
484 {
485   mLinked = false;
486   mVertexShaderId = 0;
487   mFragmentShaderId = 0;
488   mProgramId = 0;
489
490   ResetAttribsUniformCache();
491 }
492
493 bool Program::ModifiesGeometry()
494 {
495   return mModifiesGeometry;
496 }
497
498 Program::Program( ProgramCache& cache, Internal::ShaderDataPtr shaderData, bool modifiesGeometry )
499 : mCache( cache ),
500   mGlAbstraction( mCache.GetGlAbstraction() ),
501   mProjectionMatrix( NULL ),
502   mViewMatrix( NULL ),
503   mLinked( false ),
504   mVertexShaderId( 0 ),
505   mFragmentShaderId( 0 ),
506   mProgramId( 0 ),
507   mProgramData(shaderData),
508   mModifiesGeometry( modifiesGeometry )
509 {
510   // reserve space for standard attributes
511   mAttributeLocations.reserve( ATTRIB_TYPE_LAST );
512   for( int i=0; i<ATTRIB_TYPE_LAST; ++i )
513   {
514     RegisterCustomAttribute( gStdAttribs[i] );
515   }
516
517   // reserve space for standard uniforms
518   mUniformLocations.reserve( UNIFORM_TYPE_LAST );
519   // reset built in uniform names in cache
520   for( int i = 0; i < UNIFORM_TYPE_LAST; ++i )
521   {
522     RegisterUniform( gStdUniforms[ i ] );
523   }
524
525   // reset values
526   ResetAttribsUniformCache();
527 }
528
529 Program::~Program()
530 {
531   Unload();
532 }
533
534 void Program::Load()
535 {
536   DALI_ASSERT_ALWAYS( NULL != mProgramData.Get() && "Program data is not initialized" );
537   DALI_ASSERT_DEBUG( mProgramId == 0 && "mProgramId != 0, so about to leak a GL resource by overwriting it." );
538
539   LOG_GL( "CreateProgram()\n" );
540   mProgramId = CHECK_GL( mGlAbstraction, mGlAbstraction.CreateProgram() );
541
542   GLint linked = GL_FALSE;
543
544   const bool binariesSupported = mCache.IsBinarySupported();
545
546   // if shader binaries are supported and ShaderData contains compiled bytecode?
547   if( binariesSupported && mProgramData->HasBinary() )
548   {
549     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - Using Compiled Shader, Size = %d\n", mProgramData->GetBufferSize());
550
551     CHECK_GL( mGlAbstraction, mGlAbstraction.ProgramBinary(mProgramId, mCache.ProgramBinaryFormat(), mProgramData->GetBufferData(), mProgramData->GetBufferSize()) );
552
553     CHECK_GL( mGlAbstraction, mGlAbstraction.ValidateProgram(mProgramId) );
554
555     GLint success;
556     CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramiv( mProgramId, GL_VALIDATE_STATUS, &success ) );
557
558     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "ValidateProgram Status = %d\n", success);
559
560     CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramiv( mProgramId, GL_LINK_STATUS, &linked ) );
561
562     if( GL_FALSE == linked )
563     {
564       DALI_LOG_ERROR("Failed to load program binary \n");
565
566       GLint nLength;
567       CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramiv( mProgramId, GL_INFO_LOG_LENGTH, &nLength) );
568       if(nLength > 0)
569       {
570         Dali::Vector< char > szLog;
571         szLog.Reserve( nLength ); // Don't call Resize as we don't want to initialise the data, just reserve a buffer
572         CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramInfoLog( mProgramId, nLength, &nLength, szLog.Begin() ) );
573         DALI_LOG_ERROR( "Program Link Error: %s\n", szLog.Begin() );
574       }
575     }
576     else
577     {
578       mLinked = true;
579       DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "Reused binary.\n" );
580     }
581   }
582
583   // Fall back to compiling and linking the vertex and fragment sources
584   if( GL_FALSE == linked )
585   {
586     DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - Runtime compilation\n");
587     if( CompileShader( GL_VERTEX_SHADER, mVertexShaderId, mProgramData->GetVertexShader() ) )
588     {
589       if( CompileShader( GL_FRAGMENT_SHADER, mFragmentShaderId, mProgramData->GetFragmentShader() ) )
590       {
591         Link();
592
593         if( binariesSupported && mLinked )
594         {
595           GLint  binaryLength = 0;
596           GLenum binaryFormat;
597           DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "Compiled and linked.\n\nVS:\n%s\nFS:\n%s\n", mProgramData->GetVertexShader(), mProgramData->GetFragmentShader() );
598
599           CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramiv(mProgramId, GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength) );
600           DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "Program::Load() - GL_PROGRAM_BINARY_LENGTH_OES: %d\n", binaryLength);
601           if( binaryLength > 0 )
602           {
603             // Allocate space for the bytecode in ShaderData
604             mProgramData->AllocateBuffer(binaryLength);
605             // Copy the bytecode to ShaderData
606             CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramBinary(mProgramId, binaryLength, NULL, &binaryFormat, mProgramData->GetBufferData()) );
607             mCache.StoreBinary( mProgramData );
608             DALI_LOG_INFO( Debug::Filter::gShader, Debug::General, "Saved binary.\n" );
609           }
610         }
611       }
612     }
613   }
614
615   // No longer needed
616   FreeShaders();
617 }
618
619 void Program::Unload()
620 {
621   FreeShaders();
622
623   if( this == mCache.GetCurrentProgram() )
624   {
625     CHECK_GL( mGlAbstraction, mGlAbstraction.UseProgram(0) );
626
627     mCache.SetCurrentProgram( NULL );
628   }
629
630   if (mProgramId)
631   {
632     LOG_GL( "DeleteProgram(%d)\n", mProgramId );
633     CHECK_GL( mGlAbstraction, mGlAbstraction.DeleteProgram( mProgramId ) );
634     mProgramId = 0;
635   }
636
637   mLinked = false;
638
639 }
640
641 bool Program::CompileShader( GLenum shaderType, GLuint& shaderId, const char* src )
642 {
643   if (!shaderId)
644   {
645     LOG_GL( "CreateShader(%d)\n", shaderType );
646     shaderId = CHECK_GL( mGlAbstraction, mGlAbstraction.CreateShader( shaderType ) );
647     LOG_GL( "AttachShader(%d,%d)\n", mProgramId, shaderId );
648     CHECK_GL( mGlAbstraction, mGlAbstraction.AttachShader( mProgramId, shaderId ) );
649   }
650
651   LOG_GL( "ShaderSource(%d)\n", shaderId );
652   CHECK_GL( mGlAbstraction, mGlAbstraction.ShaderSource(shaderId, 1, &src, NULL ) );
653
654   LOG_GL( "CompileShader(%d)\n", shaderId );
655   CHECK_GL( mGlAbstraction, mGlAbstraction.CompileShader( shaderId ) );
656
657   GLint compiled;
658   LOG_GL( "GetShaderiv(%d)\n", shaderId );
659   CHECK_GL( mGlAbstraction, mGlAbstraction.GetShaderiv( shaderId, GL_COMPILE_STATUS, &compiled ) );
660
661   if (compiled == GL_FALSE)
662   {
663     DALI_LOG_ERROR("Failed to compile shader\n");
664     LogWithLineNumbers(src);
665
666     GLint nLength;
667     mGlAbstraction.GetShaderiv( shaderId, GL_INFO_LOG_LENGTH, &nLength);
668     if(nLength > 0)
669     {
670       Dali::Vector< char > szLog;
671       szLog.Reserve( nLength ); // Don't call Resize as we don't want to initialise the data, just reserve a buffer
672       mGlAbstraction.GetShaderInfoLog( shaderId, nLength, &nLength, szLog.Begin() );
673       DALI_LOG_ERROR( "Shader Compiler Error: %s\n", szLog.Begin() );
674     }
675
676     DALI_ASSERT_ALWAYS( 0 && "Shader compilation failure" );
677   }
678
679   return compiled != 0;
680 }
681
682 void Program::Link()
683 {
684   LOG_GL( "LinkProgram(%d)\n", mProgramId );
685   CHECK_GL( mGlAbstraction, mGlAbstraction.LinkProgram( mProgramId ) );
686
687   GLint linked;
688   LOG_GL( "GetProgramiv(%d)\n", mProgramId );
689   CHECK_GL( mGlAbstraction, mGlAbstraction.GetProgramiv( mProgramId, GL_LINK_STATUS, &linked ) );
690
691   if (linked == GL_FALSE)
692   {
693     DALI_LOG_ERROR("Shader failed to link \n");
694
695     GLint nLength;
696     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       mGlAbstraction.GetProgramInfoLog( mProgramId, nLength, &nLength, szLog.Begin() );
702       DALI_LOG_ERROR( "Shader Link Error: %s\n", szLog.Begin() );
703     }
704
705     DALI_ASSERT_ALWAYS( 0 && "Shader linking failure" );
706   }
707
708   mLinked = linked != GL_FALSE;
709 }
710
711 void Program::FreeShaders()
712 {
713   if (mVertexShaderId)
714   {
715     LOG_GL( "DeleteShader(%d)\n", mVertexShaderId );
716     CHECK_GL( mGlAbstraction, mGlAbstraction.DetachShader( mProgramId, mVertexShaderId ) );
717     CHECK_GL( mGlAbstraction, mGlAbstraction.DeleteShader( mVertexShaderId ) );
718     mVertexShaderId = 0;
719   }
720
721   if (mFragmentShaderId)
722   {
723     LOG_GL( "DeleteShader(%d)\n", mFragmentShaderId );
724     CHECK_GL( mGlAbstraction, mGlAbstraction.DetachShader( mProgramId, mFragmentShaderId ) );
725     CHECK_GL( mGlAbstraction, mGlAbstraction.DeleteShader( mFragmentShaderId ) );
726     mFragmentShaderId = 0;
727   }
728 }
729
730 void Program::ResetAttribsUniformCache()
731 {
732   // reset attribute locations
733   for( unsigned i = 0; i < mAttributeLocations.size() ; ++i )
734   {
735     mAttributeLocations[ i ].second = ATTRIB_UNKNOWN;
736   }
737
738   // reset all gl uniform locations
739   for( unsigned int i = 0; i < mUniformLocations.size(); ++i )
740   {
741     // reset gl program locations and names
742     mUniformLocations[ i ].second = UNIFORM_NOT_QUERIED;
743   }
744
745   for( unsigned int i = 0; i < mSamplerUniformLocations.Size(); ++i )
746   {
747     mSamplerUniformLocations[ i ] = UNIFORM_NOT_QUERIED;
748   }
749
750   // reset uniform caches
751   mSizeUniformCache.x = mSizeUniformCache.y = mSizeUniformCache.z = 0.f;
752
753   for( int i = 0; i < MAX_UNIFORM_CACHE_SIZE; ++i )
754   {
755     // GL initializes uniforms to 0
756     mUniformCacheInt[ i ] = 0;
757     mUniformCacheFloat[ i ] = 0.0f;
758     mUniformCacheFloat2[ i ][ 0 ] = 0.0f;
759     mUniformCacheFloat2[ i ][ 1 ] = 0.0f;
760     mUniformCacheFloat4[ i ][ 0 ] = 0.0f;
761     mUniformCacheFloat4[ i ][ 1 ] = 0.0f;
762     mUniformCacheFloat4[ i ][ 2 ] = 0.0f;
763     mUniformCacheFloat4[ i ][ 3 ] = 0.0f;
764   }
765 }
766
767 } // namespace Internal
768
769 } // namespace Dali