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 = ResStaticCast<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     if (allocator == NULL) { allocator == CommandCacheManager::GetAllocator(); }
143     resBinaryShader.ref().m_CommandAllocator = allocator;
144 
145     for (int i = 0; i < resBinaryShader.GetShaderKindsCount(); i++)
146     {
147     #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
148         GLuint shader = glCreateShader(resBinaryShader.GetShaderKinds(i));
149     #else
150         u32 shader = reinterpret_cast<u32>(resShader.ptr());
151     #endif
152         resBinaryShader.SetShaderObjects(i, shader);
153     }
154 
155     void* binaryAnalyzerBuffer = allocator->Alloc( sizeof(ShaderBinaryInfo), 4 );
156 
157     resBinaryShader.ref().m_ShaderBinaryInfo = new(binaryAnalyzerBuffer) ShaderBinaryInfo( resBinaryShader.GetBinaryData() );
158 
159     ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo();
160     shaderInfo->AnalyzeBinary();
161 
162     s32 commandSize = shaderInfo->GetCommonCommandSize();
163 
164     u32* buffer = reinterpret_cast<u32*>(allocator->Alloc( commandSize, 4 ));
165 
166     // シェーダバイナリの設定コマンドを自前で作成する。
167     s32 writtenSize = shaderInfo->BuildCommonCommand(buffer, static_cast<u32>(commandSize));
168     NW_ASSERT(writtenSize == commandSize);
169 
170     resBinaryShader.ref().m_CommandCache     = buffer;
171     resBinaryShader.ref().m_CommandCacheSize = writtenSize;
172 
173   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
174     glShaderBinary(
175         resBinaryShader.GetShaderObjectsCount(),
176         reinterpret_cast<GLuint*>(resBinaryShader.GetShaderObjects()),
177         GL_PLATFORM_BINARY_DMP,
178         resBinaryShader.GetBinaryData(),
179         resBinaryShader.GetBinaryDataCount());
180   #endif
181 
182     for (int i = 0; i < resBinaryShader.GetDescriptionsCount(); i++)
183     {
184         ResShaderProgramDescription description = resBinaryShader.GetDescriptions(i);
185         GLuint vertexShader = 0;
186         s32 vertexIndex = resBinaryShader.GetDescriptions(i).GetVertexShaderIndex();
187         NW_MINMAXLT_ASSERT(vertexIndex, 0, resBinaryShader.GetShaderObjectsCount());
188         vertexShader = resBinaryShader.GetShaderObjects(vertexIndex);
189 
190         GLuint geometryShader = 0;
191         s32 geometryIndex = resBinaryShader.GetDescriptions(i).GetGeometryShaderIndex();
192 
193         if (0 <= geometryIndex && geometryIndex < resBinaryShader.GetShaderObjectsCount())
194         {
195             geometryShader = resBinaryShader.GetShaderObjects(geometryIndex);
196         }
197 
198         resBinaryShader.GetDescriptions(i).SetVertexShaderObject( vertexShader );
199         resBinaryShader.GetDescriptions(i).SetGeometryShaderObject( geometryShader );
200         resBinaryShader.GetDescriptions(i).Setup(allocator);
201     }
202 
203     return result;
204 }
205 
206 //----------------------------------------
207 static Result
ResReferenceShader_Setup(os::IAllocator * allocator,ResShader resShader)208 ResReferenceShader_Setup(os::IAllocator* allocator, ResShader resShader)
209 {
210     Result result = RESOURCE_RESULT_OK;
211     ResReferenceShader resRefShader = ResDynamicCast<ResReferenceShader>( resShader );
212 
213     NW_ASSERT( resRefShader.IsValid() );
214     NW_ASSERT( resRefShader.GetTargetShader().IsValid() );
215 
216     ResBinaryShader binaryShader = ResDynamicCast<ResBinaryShader>(resRefShader.GetTargetShader());
217 
218     if (binaryShader.IsValid())
219     {
220         ResBinaryShader_Setup(allocator, binaryShader);
221     }
222     return result;
223 }
224 
225 //----------------------------------------
226 void
Cleanup()227 ResShader::Cleanup()
228 {
229     ResBinaryShader resBinaryShader = ResDynamicCast<ResBinaryShader>(*this);
230 
231     if (resBinaryShader.IsValid())
232     {
233         os::IAllocator* allocator = resBinaryShader.ref().m_CommandAllocator;
234 
235         if ( resBinaryShader.ref().m_CommandCache != NULL )
236         {
237             resBinaryShader.ref().m_CommandAllocator->Free( resBinaryShader.ref().m_CommandCache );
238             resBinaryShader.ref().m_CommandCache = NULL;
239             resBinaryShader.ref().m_CommandCacheSize = 0;
240         }
241 
242         if ( resBinaryShader.ref().m_ShaderBinaryInfo != NULL )
243         {
244             ShaderBinaryInfo* shaderInfo = resBinaryShader.GetShaderBinaryInfo();
245             shaderInfo->~ShaderBinaryInfo();
246             resBinaryShader.ref().m_CommandAllocator->Free( shaderInfo );
247 
248             resBinaryShader.ref().m_ShaderBinaryInfo = NULL;
249         }
250 
251         ut::SafeCleanupAll(resBinaryShader.GetDescriptions());
252 
253         for (int i = 0; i < resBinaryShader.GetShaderObjectsCount(); i++)
254         {
255             u32 shaderObject = resBinaryShader.GetShaderObjects(i);
256             if (shaderObject)
257             {
258             #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
259                 glDeleteShader(shaderObject);
260             #endif
261                 resBinaryShader.SetShaderObjects(i, 0);
262             }
263         }
264 
265         // ResShaderProgramDescription::Cleanup でこの Allocator を参照してるので最後にクリアする。
266         resBinaryShader.ref().m_CommandAllocator = NULL;
267     }
268 }
269 
270 //----------------------------------------
271 Result
Setup(os::IAllocator * allocator)272 ResShaderProgramDescription::Setup(os::IAllocator* allocator)
273 {
274     if (allocator == NULL) { allocator = CommandCacheManager::GetAllocator(); }
275 
276     NW_ASSERT( allocator == ResBinaryShader(this->GetOwnerShaderData()).ref().m_CommandAllocator );
277 
278     NW_UNUSED_VARIABLE(allocator);
279     Result result = RESOURCE_RESULT_OK;
280 
281   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
282     if (this->GetProgramObject() == NULL)
283     {
284         this->ref().m_ProgramObject = CreateProgramObject();
285 
286         ShaderUniformLocation* uniformLocation = ShaderUniformLocation::Create(allocator);
287         uniformLocation->BuildUniformLocations(this->ref().m_ProgramObject);
288         this->SetUniformLocation(uniformLocation);
289     }
290   #endif
291 
292     s32 symbolsCount = this->GetSymbolsCount();
293     for (int i = 0; i < symbolsCount; ++i)
294     {
295         ResShaderSymbol resShaderSymbol = this->GetSymbols(i);
296         NW_ASSERT(resShaderSymbol.IsValid());
297 
298         // シェーダーシンボルのLocationが-1のときのみロケーション値の取得を行います。
299         if (resShaderSymbol.GetLocation() == -1)
300         {
301             s32 regIndex = this->GetVertexUniformIndex( resShaderSymbol.GetName(), NULL );
302 
303             if (regIndex >= 0)
304             {
305                 resShaderSymbol.SetGeometryUniform( false );
306                 resShaderSymbol.SetLocation(regIndex);
307             }
308             else
309             {
310                 regIndex = this->GetGeometryUniformIndex( resShaderSymbol.GetName(), NULL );
311 
312                 if (regIndex >= 0)
313                 {
314                     resShaderSymbol.SetGeometryUniform( true );
315                     resShaderSymbol.SetLocation(regIndex);
316                 }
317                 else
318                 {
319                     resShaderSymbol.SetLocation(-1);
320                     result |= RESOURCE_RESULT_IRRELEVANT_LOCATION_SHADER_SYMBOL;
321                 }
322             }
323         }
324     }
325 
326     if ( ref().m_CommandCache )
327     {
328         return result;
329     }
330 
331     // 各シェーダプログラムの設定コマンドを自前で作成する。
332     ResBinaryShader ownerShader = ResBinaryShader( this->GetOwnerShaderData() );
333 
334     ShaderBinaryInfo* shaderInfo = ownerShader.GetShaderBinaryInfo();
335 
336     s32 vertexShaderIndex = this->GetVertexShaderIndex();
337     s32 geometryShaderIndex = this->GetGeometryShaderIndex();
338 
339     s32 commandSize = shaderInfo->GetShaderProgramCommandSize( vertexShaderIndex, geometryShaderIndex );
340     u32* buffer = reinterpret_cast<u32*>(allocator->Alloc( commandSize, 4 ));
341 
342     s32 writtenSize = shaderInfo->BuildShaderProgramCommand( vertexShaderIndex, geometryShaderIndex, buffer, commandSize );
343     NW_ASSERT( writtenSize == commandSize );
344 
345     ref().m_CommandCache     = buffer;
346     ref().m_CommandCacheSize = writtenSize;
347 
348     return result;
349 }
350 
351 //----------------------------------------
352 void
Cleanup()353 ResShaderProgramDescription::Cleanup()
354 {
355   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
356     if (this->GetUniformLocation())
357     {
358         ShaderUniformLocation* uniformLocation = static_cast<ShaderUniformLocation*>(this->GetUniformLocation());
359         uniformLocation->Destroy();
360         this->SetUniformLocation(NULL);
361     }
362   #endif
363 
364     s32 symbolsCount = this->GetSymbolsCount();
365     for (int i = 0; i < symbolsCount; ++i)
366     {
367         ResShaderSymbol resShaderSymbol = this->GetSymbols(i);
368         NW_ASSERT(resShaderSymbol.IsValid());
369 
370         resShaderSymbol.SetLocation(-1);
371         resShaderSymbol.SetGeometryUniform(false);
372     }
373 
374   #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
375     if (this->GetProgramObject())
376     {
377         if (this->GetVertexShaderObject() != 0)
378         {
379             glDetachShader(this->GetProgramObject(), this->GetVertexShaderObject());
380         }
381 
382         if (this->GetGeometryShaderObject() != 0)
383         {
384             glDetachShader(this->GetProgramObject(), this->GetGeometryShaderObject());
385         }
386 
387         glDetachShader(this->GetProgramObject(), GL_DMP_FRAGMENT_SHADER_DMP);
388 
389         glDeleteProgram(this->GetProgramObject());
390     }
391   #endif
392 
393     this->ref().m_ProgramObject = 0;
394 
395     if ( this->ref().m_CommandCache )
396     {
397         os::IAllocator* allocator = ResBinaryShader(this->GetOwnerShaderData()).ref().m_CommandAllocator;
398 
399         allocator->Free( this->ref().m_CommandCache );
400         this->ref().m_CommandCache = NULL;
401         this->ref().m_CommandCacheSize = 0;
402     }
403 }
404 
405 
406 //----------------------------------------
407 s32
GetVertexUniformIndex(const char * name,ShaderBinaryInfo::SymbolType * pSymbolType) const408 ResShaderProgramDescription::GetVertexUniformIndex(
409     const char* name,
410     ShaderBinaryInfo::SymbolType* pSymbolType
411 ) const
412 {
413     const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo();
414     NW_NULL_ASSERT( shaderInfo );
415 
416     ::std::pair<s32, ShaderBinaryInfo::SymbolType> uniformInfo =
417         shaderInfo->SearchUniformIndex( this->GetVertexShaderIndex(), name );
418 
419     if ( pSymbolType )
420     {
421         *pSymbolType = uniformInfo.second;
422     }
423 
424     return uniformInfo.first;
425 }
426 
427 //----------------------------------------
428 s32
GetGeometryUniformIndex(const char * name,ShaderBinaryInfo::SymbolType * pSymbolType) const429 ResShaderProgramDescription::GetGeometryUniformIndex(
430     const char* name,
431     ShaderBinaryInfo::SymbolType* pSymbolType
432 ) const
433 {
434     const ShaderBinaryInfo* shaderInfo = this->GetShaderBinaryInfo();
435     NW_NULL_ASSERT( shaderInfo );
436 
437     ::std::pair<s32, ShaderBinaryInfo::SymbolType> uniformInfo =
438         shaderInfo->SearchUniformIndex( this->GetGeometryShaderIndex(), name );
439 
440     if ( pSymbolType )
441     {
442         *pSymbolType = uniformInfo.second;
443     }
444 
445     return uniformInfo.first;
446 }
447 
448 
449 #if defined(NW_GFX_PROGRAM_OBJECT_ENABLED)
450 
451 //----------------------------------------
452 GLuint
CreateProgramObject()453 ResShaderProgramDescription::CreateProgramObject()
454 {
455     GLuint programObject = glCreateProgram();
456     NW_ASSERTMSG(0 != programObject, "Can't create program.");
457     AttachProgram(programObject);
458     LinkProgram(programObject);
459 
460     return programObject;
461 }
462 
463 //----------------------------------------
464 void
AttachProgram(GLuint programObject)465 ResShaderProgramDescription::AttachProgram(GLuint programObject)
466 {
467     NW_ASSERT(this->GetVertexShaderObject() != 0);
468 
469     glAttachShader(programObject, this->GetVertexShaderObject());
470 
471     if (this->GetGeometryShaderObject() != 0)
472     {
473         glAttachShader(programObject, this->GetGeometryShaderObject());
474     }
475 
476     glAttachShader(programObject, GL_DMP_FRAGMENT_SHADER_DMP);
477 
478     int index = 0;
479     for (int i = 0; i < this->GetAttributeSymbolsCount(); ++i)
480     {
481         if (const char* symbol = this->GetAttributeSymbols(i))
482         {
483             glBindAttribLocation(programObject, index, symbol);
484             ++index;
485         }
486     }
487 }
488 
489 //----------------------------------------
490 void
LinkProgram(GLuint programObject)491 ResShaderProgramDescription::LinkProgram(GLuint programObject)
492 {
493     glLinkProgram(programObject);
494     int linked;
495     glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
496     if (linked == 0)
497     {
498         glDeleteProgram(programObject);
499         NW_FATAL_ERROR("Shader Link Error");
500     }
501 
502     glValidateProgram(programObject);
503 }
504 
505 #endif
506 
507 } /* namespace res */
508 } /* namespace gfx */
509 } /* namespace nw */
510 
511