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