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