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