1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     gfx_ResShader.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 19452 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "../precompiled.h"
17 
18 #include <nw/gfx/res/gfx_ResShader.h>
19 #include <nw/gfx/res/gfx_ResGraphicsFile.h>
20 #include <nw/gfx/gfx_ShaderUniforms.h>
21 #include <nw/ut/ut_Foreach.h>
22 #include <nw/ut/ut_MoveArray.h>
23 #include <nw/gfx/gfx_ShaderBinaryInfo.h>
24 
25 namespace nw {
26 namespace gfx {
27 namespace res {
28 
29 typedef Result (*SetupFunc)(os::IAllocator* allocator, ResShader resShader);
30 
31 static Result ResBinaryShader_Setup(os::IAllocator* allocator, ResShader resShader);
32 static Result ResReferenceShader_Setup(os::IAllocator* allocator, ResShader resShader);
33 
34 static SetupFunc s_ShaderSetupTable[] =
35 {
36     ResBinaryShader_Setup,
37     ResReferenceShader_Setup
38 };
39 
40 //----------------------------------------
41 ResBinaryShader
Dereference()42 ResShader::Dereference()
43 {
44     NW_ASSERT( this->IsValid() );
45 
46     switch ( this->ref().typeInfo )
47     {
48     case ResBinaryShader::TYPE_INFO:
49         {
50             return ResStaticCast<ResBinaryShader>(*this);
51         }
52 
53     case ResReferenceShader::TYPE_INFO:
54         {
55             ResReferenceShader resRefShader = ResStaticCast<ResReferenceShader>( *this );
56 
57             NW_ASSERT( resRefShader.GetTargetShader().IsValid() );
58             return resRefShader.GetTargetShader().Dereference();
59         }
60 
61     default:
62         {
63             return ResBinaryShader( NULL );
64         }
65     }
66 }
67 
68 
69 //----------------------------------------
70 const ResBinaryShader
Dereference() const71 ResShader::Dereference() const
72 {
73     NW_ASSERT( this->IsValid() );
74 
75     switch ( this->ref().typeInfo )
76     {
77     case ResBinaryShader::TYPE_INFO:
78         {
79             return ResStaticCast<ResBinaryShader>(*this);
80         }
81 
82     case ResReferenceShader::TYPE_INFO:
83         {
84             ResReferenceShader resRefShader = ResDynamicCast<ResReferenceShader>( *this );
85 
86             NW_ASSERT( resRefShader.GetTargetShader().IsValid() );
87             return resRefShader.GetTargetShader().Dereference();
88         }
89 
90     default:
91         {
92             return ResBinaryShader( NULL );
93         }
94     }
95 }
96 
97 //----------------------------------------
98 Result
Setup(os::IAllocator * allocator,ResGraphicsFile graphicsFile)99 ResShader::Setup(os::IAllocator* allocator, ResGraphicsFile graphicsFile)
100 {
101     NW_UNUSED_VARIABLE(graphicsFile);
102 
103     NW_ASSERT( internal::ResCheckRevision( *this ) );
104 
105     Result result = RESOURCE_RESULT_OK;
106     switch ( this->ref().typeInfo )
107     {
108     case ResBinaryShader::TYPE_INFO:
109         {
110             result |= s_ShaderSetupTable[0]( allocator, *this );
111         }
112         break;
113     case ResReferenceShader::TYPE_INFO:
114         {
115             result |= s_ShaderSetupTable[1]( allocator, *this );
116         }
117         break;
118     default:
119         {
120         }
121     }
122     return result;
123 }
124 
125 //----------------------------------------
126 static Result
ResBinaryShader_Setup(os::IAllocator * allocator,ResShader resShader)127 ResBinaryShader_Setup(os::IAllocator* allocator, ResShader resShader)
128 {
129     Result result = RESOURCE_RESULT_OK;
130 
131     ResBinaryShader resBinaryShader = ResDynamicCast<ResBinaryShader>(resShader);
132 
133     NW_ASSERT(resBinaryShader.GetShaderObjectsCount() != 0);
134 
135     u32 shaderObjects = resBinaryShader.GetShaderObjects(0);
136 
137     if (shaderObjects != NULL)
138     {
139         return result;
140     }
141 
142     for (int i = 0; i < resBinaryShader.GetShaderKindsCount(); i++)
143     {
144         GLuint shader = glCreateShader(resBinaryShader.GetShaderKinds(i));
145         resBinaryShader.SetShaderObjects(i, shader);
146     }
147 
148     void* binaryAnalyzerBuffer = CommandCacheManager::Allocate( sizeof(ShaderBinaryInfo) );
149 
150     resBinaryShader.ref().m_ShaderBinaryInfo = new(binaryAnalyzerBuffer) ShaderBinaryInfo( resBinaryShader.GetBinaryData() );
151 
152     ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo();
153     shaderInfo->AnalyzeBinary();
154 
155     // シェーダバイナリの設定コマンドを自前で作成する。
156     internal::CommandCacheBuilder builder;
157     builder.Begin();
158 
159     {
160         shaderInfo->BuildCommonCommand();
161     }
162 
163     builder.End();
164 
165     resBinaryShader.ref().m_CommandCache     = builder.AllocAndCopy();
166     resBinaryShader.ref().m_CommandCacheSize = builder.GetSize();
167 
168     builder.Rollback();
169 
170   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
171     glShaderBinary(
172         resBinaryShader.GetShaderObjectsCount(),
173         reinterpret_cast<GLuint*>(resBinaryShader.GetShaderObjects()),
174         GL_PLATFORM_BINARY_DMP,
175         resBinaryShader.GetBinaryData(),
176         resBinaryShader.GetBinaryDataCount());
177   #endif
178 
179     for (int i = 0; i < resBinaryShader.GetDescriptionsCount(); i++)
180     {
181         ResShaderProgramDescription description = resBinaryShader.GetDescriptions(i);
182         GLuint vertexShader = 0;
183         s32 vertexIndex = resBinaryShader.GetDescriptions(i).GetVertexShaderIndex();
184         NW_MINMAXLT_ASSERT(vertexIndex, 0, resBinaryShader.GetShaderObjectsCount());
185         vertexShader = resBinaryShader.GetShaderObjects(vertexIndex);
186 
187         GLuint geometryShader = 0;
188         s32 geometryIndex = resBinaryShader.GetDescriptions(i).GetGeometryShaderIndex();
189 
190         if (0 <= geometryIndex && geometryIndex < resBinaryShader.GetShaderObjectsCount())
191         {
192             geometryShader = resBinaryShader.GetShaderObjects(geometryIndex);
193         }
194 
195         resBinaryShader.GetDescriptions(i).SetVertexShaderObject( vertexShader );
196         resBinaryShader.GetDescriptions(i).SetGeometryShaderObject( geometryShader );
197         resBinaryShader.GetDescriptions(i).Setup(allocator);
198     }
199 
200     return result;
201 }
202 
203 //----------------------------------------
204 static Result
ResReferenceShader_Setup(os::IAllocator * allocator,ResShader resShader)205 ResReferenceShader_Setup(os::IAllocator* allocator, ResShader resShader)
206 {
207     Result result = RESOURCE_RESULT_OK;
208     ResReferenceShader resRefShader = ResDynamicCast<ResReferenceShader>( resShader );
209 
210     NW_ASSERT( resRefShader.IsValid() );
211     NW_ASSERT( resRefShader.GetTargetShader().IsValid() );
212 
213     ResBinaryShader binaryShader = ResDynamicCast<ResBinaryShader>(resRefShader.GetTargetShader());
214 
215     if (binaryShader.IsValid())
216     {
217         ResBinaryShader_Setup(allocator, binaryShader);
218     }
219     return result;
220 }
221 
222 //----------------------------------------
223 void
Cleanup()224 ResShader::Cleanup()
225 {
226     ResBinaryShader resBinaryShader = ResDynamicCast<ResBinaryShader>(*this);
227 
228     if (resBinaryShader.IsValid())
229     {
230         if ( resBinaryShader.ref().m_CommandCache != NULL )
231         {
232             CommandCacheManager::Free( resBinaryShader.ref().m_CommandCache );
233             resBinaryShader.ref().m_CommandCache = NULL;
234             resBinaryShader.ref().m_CommandCacheSize = 0;
235         }
236 
237         if ( resBinaryShader.ref().m_ShaderBinaryInfo != NULL )
238         {
239             ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo();
240             shaderInfo->~ShaderBinaryInfo();
241             CommandCacheManager::Free( shaderInfo );
242 
243             resBinaryShader.ref().m_ShaderBinaryInfo = NULL;
244         }
245 
246         ut::SafeCleanupAll(resBinaryShader.GetDescriptions());
247 
248         for (int i = 0; i < resBinaryShader.GetShaderObjectsCount(); i++)
249         {
250             u32 shaderObject = resBinaryShader.GetShaderObjects(i);
251             if (shaderObject)
252             {
253                 glDeleteShader(shaderObject);
254                 resBinaryShader.SetShaderObjects(i, 0);
255             }
256         }
257     }
258 }
259 
260 //----------------------------------------
261 Result
Setup(os::IAllocator * allocator)262 ResShaderProgramDescription::Setup(os::IAllocator* allocator)
263 {
264     NW_UNUSED_VARIABLE(allocator);
265     Result result = RESOURCE_RESULT_OK;
266 
267   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
268     if (this->GetProgramObject() == NULL)
269     {
270         this->ref().m_ProgramObject = CreateProgramObject();
271 
272         ShaderUniformLocation* uniformLocation = ShaderUniformLocation::Create(allocator);
273         uniformLocation->BuildUniformLocations(this->ref().m_ProgramObject);
274         this->SetUniformLocation(uniformLocation);
275     }
276   #endif
277 
278     s32 symbolsCount = this->GetSymbolsCount();
279     for (int i = 0; i < symbolsCount; ++i)
280     {
281         ResShaderSymbol resShaderSymbol = this->GetSymbols(i);
282         NW_ASSERT(resShaderSymbol.IsValid());
283 
284         // シェーダーシンボルのLocationが-1のときのみロケーション値の取得を行います。
285         if (resShaderSymbol.GetLocation() == -1)
286         {
287             s32 regIndex = this->GetVertexUniformIndex( resShaderSymbol.GetName(), NULL );
288 
289             if (regIndex >= 0)
290             {
291                 resShaderSymbol.SetGeometryUniform( false );
292                 resShaderSymbol.SetLocation(regIndex);
293             }
294             else
295             {
296                 regIndex = this->GetGeometryUniformIndex( resShaderSymbol.GetName(), NULL );
297 
298                 if (regIndex >= 0)
299                 {
300                     resShaderSymbol.SetGeometryUniform( true );
301                     resShaderSymbol.SetLocation(regIndex);
302                 }
303                 else
304                 {
305                     resShaderSymbol.SetLocation(-1);
306                     result |= RESOURCE_RESULT_IRRELEVANT_LOCATION_SHADER_SYMBOL;
307                 }
308             }
309         }
310     }
311 
312     if ( ref().m_CommandCache )
313     {
314         return result;
315     }
316 
317     // 各シェーダプログラムの設定コマンドを自前で作成する。
318     internal::CommandCacheBuilder builder;
319     builder.Begin();
320 
321     {
322         ResBinaryShader ownerShader = ResBinaryShader( this->GetOwnerShaderData() );
323 
324         ShaderBinaryInfo* shaderInfo = ownerShader.GetShaderBinaryInfo();
325 
326         s32 vertexShaderIndex = this->GetVertexShaderIndex();
327         s32 geometryShaderIndex = this->GetGeometryShaderIndex();
328 
329         shaderInfo->BuildShaderProgramCommand( vertexShaderIndex, geometryShaderIndex );
330     }
331 
332     builder.End();
333 
334     ref().m_CommandCache     = builder.AllocAndCopy();
335     ref().m_CommandCacheSize = builder.GetSize();
336 
337     builder.Rollback();
338 
339     return result;
340 }
341 
342 //----------------------------------------
343 void
Cleanup()344 ResShaderProgramDescription::Cleanup()
345 {
346   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
347     if (this->GetUniformLocation())
348     {
349         ShaderUniformLocation* uniformLocation = static_cast<ShaderUniformLocation*>(this->GetUniformLocation());
350         uniformLocation->Destroy();
351         this->SetUniformLocation(NULL);
352     }
353   #endif
354 
355     s32 symbolsCount = this->GetSymbolsCount();
356     for (int i = 0; i < symbolsCount; ++i)
357     {
358         ResShaderSymbol resShaderSymbol = this->GetSymbols(i);
359         NW_ASSERT(resShaderSymbol.IsValid());
360 
361         resShaderSymbol.SetLocation(-1);
362         resShaderSymbol.SetGeometryUniform(false);
363     }
364 
365   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
366     if (this->GetProgramObject())
367     {
368         if (this->GetVertexShaderObject() != 0)
369         {
370             glDetachShader(this->GetProgramObject(), this->GetVertexShaderObject());
371         }
372 
373         if (this->GetGeometryShaderObject() != 0)
374         {
375             glDetachShader(this->GetProgramObject(), this->GetGeometryShaderObject());
376         }
377 
378         glDetachShader(this->GetProgramObject(), GL_DMP_FRAGMENT_SHADER_DMP);
379 
380         glDeleteProgram(this->GetProgramObject());
381     }
382   #endif
383 
384     this->ref().m_ProgramObject = 0;
385 
386     if ( this->ref().m_CommandCache )
387     {
388         CommandCacheManager::Free( this->ref().m_CommandCache );
389         this->ref().m_CommandCache = NULL;
390         this->ref().m_CommandCacheSize = 0;
391     }
392 }
393 
394 
395 //----------------------------------------
396 s32
GetVertexUniformIndex(const char * name,ShaderBinaryInfo::SymbolType * pSymbolType) const397 ResShaderProgramDescription::GetVertexUniformIndex(
398     const char* name,
399     ShaderBinaryInfo::SymbolType* pSymbolType
400 ) const
401 {
402     const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo();
403     NW_NULL_ASSERT( shaderInfo );
404 
405     ::std::pair<s32, ShaderBinaryInfo::SymbolType> uniformInfo =
406         shaderInfo->SearchUniformIndex( this->GetVertexShaderIndex(), name );
407 
408     if ( pSymbolType )
409     {
410         *pSymbolType = uniformInfo.second;
411     }
412 
413     return uniformInfo.first;
414 }
415 
416 //----------------------------------------
417 s32
GetGeometryUniformIndex(const char * name,ShaderBinaryInfo::SymbolType * pSymbolType) const418 ResShaderProgramDescription::GetGeometryUniformIndex(
419     const char* name,
420     ShaderBinaryInfo::SymbolType* pSymbolType
421 ) const
422 {
423     const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo();
424     NW_NULL_ASSERT( shaderInfo );
425 
426     ::std::pair<s32, ShaderBinaryInfo::SymbolType> uniformInfo =
427         shaderInfo->SearchUniformIndex( this->GetGeometryShaderIndex(), name );
428 
429     if ( pSymbolType )
430     {
431         *pSymbolType = uniformInfo.second;
432     }
433 
434     return uniformInfo.first;
435 }
436 
437 
438 #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
439 
440 //----------------------------------------
441 GLuint
CreateProgramObject()442 ResShaderProgramDescription::CreateProgramObject()
443 {
444     GLuint programObject = glCreateProgram();
445     NW_ASSERTMSG(0 != programObject, "Can't create program.");
446     AttachProgram(programObject);
447     LinkProgram(programObject);
448 
449     return programObject;
450 }
451 
452 //----------------------------------------
453 void
AttachProgram(GLuint programObject)454 ResShaderProgramDescription::AttachProgram(GLuint programObject)
455 {
456     NW_ASSERT(this->GetVertexShaderObject() != 0);
457 
458     glAttachShader(programObject, this->GetVertexShaderObject());
459 
460     if (this->GetGeometryShaderObject() != 0)
461     {
462         glAttachShader(programObject, this->GetGeometryShaderObject());
463     }
464 
465     glAttachShader(programObject, GL_DMP_FRAGMENT_SHADER_DMP);
466 
467     int index = 0;
468     for (int i = 0; i < this->GetAttributeSymbolsCount(); ++i)
469     {
470         if (const char* symbol = this->GetAttributeSymbols(i))
471         {
472             glBindAttribLocation(programObject, index, symbol);
473             ++index;
474         }
475     }
476 }
477 
478 //----------------------------------------
479 void
LinkProgram(GLuint programObject)480 ResShaderProgramDescription::LinkProgram(GLuint programObject)
481 {
482     glLinkProgram(programObject);
483     int linked;
484     glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
485     if (linked == 0)
486     {
487         glDeleteProgram(programObject);
488         NW_FATAL_ERROR("Shader Link Error");
489     }
490 
491     glValidateProgram(programObject);
492 }
493 
494 #endif
495 
496 } /* namespace res */
497 } /* namespace gfx */
498 } /* namespace nw */
499 
500