1 /*---------------------------------------------------------------------------*
2 Project: NintendoWare
3 File: gfx_ShaderBinaryInfo.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: 22599 $
14 *---------------------------------------------------------------------------*/
15
16 #include "precompiled.h"
17
18 #include <nw/gfx/gfx_CommandUtil.h>
19 #include <nw/gfx/gfx_ShaderBinaryInfo.h>
20
21 namespace nw
22 {
23 namespace gfx
24 {
25
26 //---------------------------------------------------------------------------
27 void
AnalyzeBinary()28 ShaderBinaryInfo::AnalyzeBinary()
29 {
30 const u32* binary = m_pShaderBinary;
31
32 NW_ASSERT( *binary == ut::ReverseEndian('DVLB') );
33 ++binary;
34
35 NW_ASSERT( *binary < EXE_IMAGE_MAX );
36 m_ExeImageCount = *binary;
37 ++binary;
38
39 for ( int i = 0; i < m_ExeImageCount; ++i )
40 {
41 m_ExeImageInfo[ i ] = reinterpret_cast< const ExeImageInfo* >( (u8*)m_pShaderBinary + *binary );
42 NW_ASSERT( m_ExeImageInfo[ i ]->signature == ut::ReverseEndian('DVLE') );
43
44 if ( m_ExeImageInfo[ i ]->isGeometryShader )
45 {
46 ++m_GeometryShaderCount;
47 }
48
49 ++binary;
50 }
51
52 const u32* packageInfo = binary;
53 NW_ASSERT( *binary == ut::ReverseEndian('DVLP') ); // DVLP
54 ++binary;
55 ++binary;
56
57 m_pInstruction = static_cast<const u32*>( ut::AddOffsetToPtr( packageInfo, *binary ) );
58 ++binary;
59
60 m_InstructionCount = *binary;
61 ++binary;
62
63 const u32* swizzle = static_cast<const u32*>( ut::AddOffsetToPtr( packageInfo, *binary ) );
64 ++binary;
65
66 m_SwizzleCount = *binary;
67 NW_ASSERT( m_SwizzleCount < SWIZZLE_PATTERN_MAX );
68 ++binary;
69
70 // リンカ用のメタ情報はスキップして値だけ保存する。
71 for ( int i = 0; i < m_SwizzleCount; i++ )
72 {
73 m_Swizzle[ i ] = swizzle[ i * 2 ];
74 }
75 }
76
77
78 //---------------------------------------------------------------------------
79 s32
BuildCommonCommand(u32 * bufferAddress,u32 bufferSize)80 ShaderBinaryInfo::BuildCommonCommand( u32* bufferAddress, u32 bufferSize )
81 {
82 SafeBuffer buffer(bufferAddress, bufferSize);
83
84 // シェーダバイナリの転送コマンドを生成します。
85 // その他の頂点の設定コマンド等は、ShaderProgramDescription 側に持ちます。
86
87 // ジオメトリシェーダのインストラクションの前方部分には、
88 // 頂点シェーダと同じ命令が格納されているので、ResShaderBinary が
89 // 同じであればシェーダバイナリの再転送は必要ない。
90 //
91 // この為、ジオメトリシェーダがある場合には必ずジオメトリ分のバイナリも転送する。
92
93 if ( this->GetGeometryShaderCount() > 0 )
94 {
95 this->PutEnableMirroringShaderSetting( buffer, false );
96 }
97 else
98 {
99 this->PutEnableMirroringShaderSetting( buffer, true );
100 }
101
102 this->BuildSwizzleCommand( buffer );
103 this->BuildProgramCommand( buffer );
104
105 return buffer.UsedSize();
106 }
107
108
109 //---------------------------------------------------------------------------
110 void
BuildProgramCommand(SafeBuffer & buffer) const111 ShaderBinaryInfo::BuildProgramCommand( SafeBuffer& buffer ) const
112 {
113 // あらかじめ、PutEnableMirroringShaderSetting でミラーリングが有効に
114 // 設定した状態で呼び出される事が前提となっています。
115
116 // この関数から抜ける際には、MirroringShaderSetting の値は不定となります。
117
118 // まず先頭から512命令以内の部分は、頂点・ジオメトリ共用部分として送信する。
119
120 enum { VS_INSTRUCTION_MAX = 512 };
121
122 u32 vertexInstructionCount = ut::Min( m_InstructionCount, u32(VS_INSTRUCTION_MAX) );
123
124 const u32 VS_COMMAND[] =
125 {
126 0,
127 internal::MakeCommandHeader( PICA_REG_VS_PROG_ADDR, 1, false, 0xF )
128 };
129
130 buffer.Write( &VS_COMMAND[0], sizeof(VS_COMMAND) );
131
132 // 頂点部分のインストラクションのロード。
133 this->PutLoadCommand( buffer,
134 PICA_REG_VS_PROG_DATA0,
135 &m_pInstruction[ 0 ],
136 vertexInstructionCount );
137
138 const u32 VS_RENEWAL_COMMAND[] =
139 {
140 1,
141 internal::MakeCommandHeader( PICA_REG_VS_PROG_RENEWAL_END, 1, false, 0xF )
142 };
143
144 buffer.Write( &VS_RENEWAL_COMMAND[0], sizeof(VS_RENEWAL_COMMAND) );
145
146 if ( this->GetGeometryShaderCount() > 0 )
147 {
148 const u32 GS_COMMAND[] =
149 {
150 0,
151 internal::MakeCommandHeader( PICA_REG_GS_PROG_ADDR, 1, false, 0xF )
152 };
153
154 buffer.Write( &GS_COMMAND[0], sizeof(GS_COMMAND) );
155
156 // 頂点部分のインストラクションのロード。
157 this->PutLoadCommand( buffer,
158 PICA_REG_GS_PROG_DATA0,
159 &m_pInstruction[ 0 ],
160 m_InstructionCount );
161
162 const u32 GS_RENEWAL_COMMAND[] =
163 {
164 1,
165 internal::MakeCommandHeader( PICA_REG_GS_PROG_RENEWAL_END, 1, false, 0xF )
166 };
167
168 buffer.Write( &GS_RENEWAL_COMMAND[0], sizeof(GS_RENEWAL_COMMAND) );
169 }
170 }
171
172
173 //---------------------------------------------------------------------------
174 void
BuildSwizzleCommand(SafeBuffer & buffer) const175 ShaderBinaryInfo::BuildSwizzleCommand( SafeBuffer& buffer ) const
176 {
177 // あらかじめ、PutEnableMirroringShaderSetting でミラーリングが有効に
178 // 設定した状態で呼び出される事が前提となっています。
179
180 const u32 COMMAND[] =
181 {
182 0,
183 internal::MakeCommandHeader( PICA_REG_VS_PROG_SWIZZLE_ADDR, 1, false, 0xF )
184 };
185
186 buffer.Write( &COMMAND[0], sizeof(COMMAND) );
187
188 NW_ASSERT( m_SwizzleCount > 0 );
189
190 // Swizzleパターンのロード
191 this->PutLoadCommand( buffer,
192 PICA_REG_VS_PROG_SWIZZLE_DATA0,
193 &m_Swizzle[0],
194 m_SwizzleCount );
195
196 if ( this->GetGeometryShaderCount() > 0 )
197 {
198 const u32 GS_COMMAND[] =
199 {
200 0,
201 internal::MakeCommandHeader( PICA_REG_GS_PROG_SWIZZLE_ADDR, 1, false, 0xF )
202 };
203
204 buffer.Write( &GS_COMMAND[0], sizeof(GS_COMMAND) );
205
206 NW_ASSERT( m_SwizzleCount > 0 );
207
208 // Swizzleパターンのロード
209 this->PutLoadCommand( buffer,
210 PICA_REG_GS_PROG_SWIZZLE_DATA0,
211 &m_Swizzle[0],
212 m_SwizzleCount );
213 }
214 }
215
216
217 //---------------------------------------------------------------------------
218 s32
BuildShaderProgramCommand(s32 vertexIndex,s32 geometryIndex,u32 * bufferAddress,u32 bufferSize)219 ShaderBinaryInfo::BuildShaderProgramCommand( s32 vertexIndex, s32 geometryIndex, u32* bufferAddress, u32 bufferSize )
220 {
221 SafeBuffer buffer(bufferAddress, bufferSize);
222
223 NW_ASSERT( ! this->IsGeometryShader( vertexIndex ) );
224 NW_ASSERT( 0 <= vertexIndex && vertexIndex < this->GetShaderCount() );
225 NW_ASSERT( geometryIndex < 0 || this->IsGeometryShader( geometryIndex ) );
226 NW_ASSERT( geometryIndex < this->GetShaderCount() );
227
228 if ( geometryIndex < 0 )
229 {
230 this->PutEnableMirroringShaderSetting( buffer, true );
231 }
232 else
233 {
234 this->PutEnableMirroringShaderSetting( buffer, false );
235 }
236
237 this->BuildConstRegCommand( buffer, vertexIndex );
238
239 if ( geometryIndex >= 0 )
240 {
241 this->BuildConstRegCommand( buffer, geometryIndex );
242 }
243
244 this->BuildOutAttrCommand( buffer, vertexIndex, geometryIndex );
245
246 return buffer.UsedSize();
247 }
248
249
250 //---------------------------------------------------------------------------
251 void
BuildPrepareCommand(SafeBuffer & buffer) const252 ShaderBinaryInfo::BuildPrepareCommand( SafeBuffer& buffer ) const
253 {
254 NW_UNUSED_VARIABLE(buffer);
255
256 // tugal では次のコマンドを生成している。
257 // ・ジオメトリシェーダの有効化とパイプライン掃除用のダミーコマンド。
258 // ・0x25e[8:9] へのプリミティブ形状設定。
259 // ・0x244 への頂点・ジオメトリ設定のミラーリング設定。
260 // NW では、これらは必要な場合のみ設定するようにする。
261 }
262
263
264 //---------------------------------------------------------------------------
265 void
BuildConstRegCommand(SafeBuffer & buffer,s32 shaderIndex) const266 ShaderBinaryInfo::BuildConstRegCommand( SafeBuffer& buffer, s32 shaderIndex ) const
267 {
268 bool isGeometry = this->IsGeometryShader( shaderIndex );
269
270 u32 regFloat = PICA_REG_VS_FLOAT_ADDR; // 0x2c0
271 u32 regInteger = PICA_REG_VS_INT0; // 0x2b1
272
273 if ( isGeometry )
274 {
275 regFloat = PICA_REG_GS_FLOAT_ADDR; // 0x290
276 regInteger = PICA_REG_GS_INT0; // 0x281
277 }
278
279 // プログラム情報
280 const ExeImageInfo* exeInfo = m_ExeImageInfo[ shaderIndex ];
281
282 // 定数レジスタ情報
283 struct SetupInfo
284 {
285 u16 type;
286 u16 index;
287 u32 value[4];
288 };
289
290 enum { TYPE_BOOL = 0, TYPE_INT = 1, TYPE_FLOAT = 2 };
291
292 const SetupInfo* setupInfoTable =
293 static_cast<const SetupInfo*>( ut::AddOffsetToPtr( exeInfo, exeInfo->setupOffset) );
294
295 // 定数レジスタのコマンド生成
296 for ( int i = 0; i < exeInfo->setupCount; ++i )
297 {
298 const SetupInfo& info = setupInfoTable[ i ];
299 const u32* value = info.value;
300
301 switch ( info.type )
302 {
303 case TYPE_BOOL:
304 break;
305
306 case TYPE_INT:
307 {
308 const u32 COMMAND[] =
309 {
310 value[ 0 ],
311 internal::MakeCommandHeader(regInteger + info.index, 1, false, 0xF)
312 };
313
314 buffer.Write( &COMMAND[0], sizeof(COMMAND) );
315 }
316 break;
317
318 case TYPE_FLOAT:
319
320 {
321 const u32 COMMAND[] =
322 {
323 info.index, // 24 bit モード
324 internal::MakeCommandHeader( regFloat, 4, true, 0xF ),
325 ( value[ 3 ] << 8 & 0xffffff00 ) | ( value[ 2 ] >> 16 & 0x000000ff ),
326 ( value[ 2 ] << 16 & 0xffff0000 ) | ( value[ 1 ] >> 8 & 0x0000ffff ),
327 ( value[ 1 ] << 24 & 0xff000000 ) | ( value[ 0 ] >> 0 & 0x00ffffff ),
328 0, // padding
329 };
330
331 buffer.Write( &COMMAND[0], sizeof(COMMAND) );
332 }
333 break;
334 }
335 }
336 }
337
338 //---------------------------------------------------------------------------
339 void
BuildOutAttrCommand(SafeBuffer & buffer,s32 vertexIndex,s32 geometryIndex) const340 ShaderBinaryInfo::BuildOutAttrCommand( SafeBuffer& buffer, s32 vertexIndex, s32 geometryIndex ) const
341 {
342 bool hasGeometry = geometryIndex >= 0;
343
344 u32 vertexOutputMask;
345 u32 vertexOutputNum;
346 u32 shaderOutputMask;
347 u32 shaderOutputNum;
348 u32 shaderOutputMap[7];
349 u32 clockControl = 0x01030703; // クロック制御はひとまず全て有効。
350 u32 vertexEntry;
351 u32 geometryInputNum;
352 u32 geometryEntry;
353 bool isTextureOutput = false;
354
355 this->GetOutputRegisterNum( vertexIndex, &vertexOutputNum, &vertexOutputMask );
356 this->GetShaderOutputRegisterNum( vertexIndex, geometryIndex, &shaderOutputNum, &shaderOutputMask );
357 this->GetShaderOutputRegisterMap( vertexIndex, geometryIndex, &shaderOutputMap[0] );
358 vertexEntry = this->GetEntryAddress( vertexIndex );
359
360 u32 GEOMETRY_SETTING_COMMAND[] =
361 {
362 0x00000000, 0x000f0252,
363 0x00000000, 0x00010254,
364 0x00000000, 0x00080229,
365 0x00000000, 0x00020289,
366 };
367
368 if ( hasGeometry )
369 {
370 enum { IDX_REG_252 = 0, IDX_REG_254 = 2, IDX_REG_229 = 4, IDX_REG_289 = 6 };
371
372 geometryInputNum = vertexOutputNum;
373 geometryEntry = this->GetEntryAddress( geometryIndex );
374
375 u32 geometryMode = this->GetGeometryDataMode( geometryIndex );
376
377 GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= geometryMode & 0x3;
378
379 switch ( geometryMode )
380 {
381 case 0: // normal mode
382 break;
383 case 1: // subdivision mode
384 {
385 GEOMETRY_SETTING_COMMAND[ IDX_REG_229 ] |= 1u << 31;
386 GEOMETRY_SETTING_COMMAND[ IDX_REG_289 ] |= 1 << 8;
387
388 u32 geomMainVertexNum = this->GetGeometryMainVertexNum( geometryIndex );
389
390 GEOMETRY_SETTING_COMMAND[ IDX_REG_254 ] |= geomMainVertexNum;
391 }
392 break;
393 case 2: // constant data mode
394 {
395 GEOMETRY_SETTING_COMMAND[ IDX_REG_289 ] |= 1 << 8;
396
397 u32 geomPatchSize = this->GetGeometryPatchSize( geometryIndex );
398 u32 geomStartIndex = this->GetGeometryStartIndex( geometryIndex );
399
400 GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= (geomPatchSize - 1) << 8;
401 GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= (vertexOutputNum - 1) << 12;
402 GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= geomStartIndex << 16;
403 GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= 1 << 24;
404 }
405 break;
406 default:
407 NW_FATAL_ERROR("Invalid geometry mode");
408 }
409 }
410
411 // 頂点入出力レジスタ設定
412 const u32 SHADER_PROGRAM_COMMAND[] =
413 {
414 vertexOutputMask, 0x000f02bd, // 頂点出力マスク (vtx)
415 vertexOutputNum - 1, 0x000f0251, // 出力レジスタ数 - 1 (vtx)
416 vertexOutputNum - 1, 0x000f024a, // 出力レジスタ数 - 1 (vtx)
417 shaderOutputNum - 1, 0x0001025e, // 出力レジスタ数 - 1 (vtx/geom)
418 shaderOutputNum, 0x000f004f, // 出力レジスタ数 (vtx/geom)
419 shaderOutputMap[0], 0x806f0050, // 出力レジスタ0のフォーマット
420 shaderOutputMap[1], shaderOutputMap[2], // 出力レジスタ1,2のフォーマット
421 shaderOutputMap[3], shaderOutputMap[4], // 出力レジスタ3,4のフォーマット
422 shaderOutputMap[5], shaderOutputMap[6], // 出力レジスタ5,6のフォーマット
423 0x00000001, 0x000f0064, // テクスチャ座標が出力される場合は1
424 clockControl, 0x000f006f, // シェーダクロック制御
425 0x7fff0000 | vertexEntry, 0x000f02ba, // 頂点シェーダ開始アドレス
426 };
427
428 u32 geometrySettingCommandSize = sizeof(GEOMETRY_SETTING_COMMAND) - sizeof(u32) * (hasGeometry ? 0 : 2);
429 buffer.Write( &GEOMETRY_SETTING_COMMAND[0], geometrySettingCommandSize );
430 buffer.Write( &SHADER_PROGRAM_COMMAND[0], sizeof(SHADER_PROGRAM_COMMAND) );
431
432 if ( hasGeometry )
433 {
434 // ジオメトリ入出力レジスタ設定
435 const u32 GEOMETRY_COMMAND[] =
436 {
437 shaderOutputMask, 0x000f028d, // ジオメトリ出力マスク
438 0x08000000 | (geometryInputNum - 1), 0x00090289, // ジオメトリシェーダの入力レジスタ数 - 1
439 0x7fff0000 | geometryEntry, 0x000f028a, // ジオメトリシェーダの開始アドレス
440 };
441
442 buffer.Write( &GEOMETRY_COMMAND[0], sizeof(GEOMETRY_COMMAND) );
443 }
444 }
445
446 //---------------------------------------------------------------------------
447 void
PutEnableMirroringShaderSetting(SafeBuffer & buffer,bool enableMirroring) const448 ShaderBinaryInfo::PutEnableMirroringShaderSetting(SafeBuffer& buffer, bool enableMirroring) const
449 {
450 const u32 COMMAND[] =
451 {
452 enableMirroring ? 0 : 1,
453 internal::MakeCommandHeader( PICA_REG_VS_COM_MODE, 1, false, 0x1 )
454 };
455
456 buffer.Write( &COMMAND[0], sizeof(COMMAND) );
457 }
458
459 //---------------------------------------------------------------------------
460 void
PutLoadCommand(SafeBuffer & buffer,u32 regAddr,const u32 * src,u32 count) const461 ShaderBinaryInfo::PutLoadCommand(
462 SafeBuffer& buffer,
463 u32 regAddr,
464 const u32* src,
465 u32 count
466 ) const
467 {
468 enum { WRITE_MAX = 128 };
469
470 u32 restCount = count;
471 u32 index = 0;
472
473 while ( true )
474 {
475 u32 countPerCommand = ut::Min( restCount, u32(WRITE_MAX) );
476
477 const u32 COMMAND[] =
478 {
479 src[index],
480 internal::MakeCommandHeader( regAddr, countPerCommand, false, 0xF )
481 };
482
483 buffer.Write( &COMMAND[0], sizeof(COMMAND) );
484 buffer.Write( &src[index + 1], (countPerCommand - 1) * sizeof(u32) );
485
486 if ( (countPerCommand % 2) == 0 )
487 {
488 buffer.Write( 0 ); // パディング挿入
489 }
490
491 index += countPerCommand;
492 restCount -= countPerCommand;
493
494 if (restCount == 0)
495 {
496 break;
497 }
498 }
499 }
500
501 } // namespace gfx
502 } // namespace nw
503