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