/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ShaderBinaryInfo.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 22599 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include namespace nw { namespace gfx { //--------------------------------------------------------------------------- void ShaderBinaryInfo::AnalyzeBinary() { const u32* binary = m_pShaderBinary; NW_ASSERT( *binary == ut::ReverseEndian('DVLB') ); ++binary; NW_ASSERT( *binary < EXE_IMAGE_MAX ); m_ExeImageCount = *binary; ++binary; for ( int i = 0; i < m_ExeImageCount; ++i ) { m_ExeImageInfo[ i ] = reinterpret_cast< const ExeImageInfo* >( (u8*)m_pShaderBinary + *binary ); NW_ASSERT( m_ExeImageInfo[ i ]->signature == ut::ReverseEndian('DVLE') ); if ( m_ExeImageInfo[ i ]->isGeometryShader ) { ++m_GeometryShaderCount; } ++binary; } const u32* packageInfo = binary; NW_ASSERT( *binary == ut::ReverseEndian('DVLP') ); // DVLP ++binary; ++binary; m_pInstruction = static_cast( ut::AddOffsetToPtr( packageInfo, *binary ) ); ++binary; m_InstructionCount = *binary; ++binary; const u32* swizzle = static_cast( ut::AddOffsetToPtr( packageInfo, *binary ) ); ++binary; m_SwizzleCount = *binary; NW_ASSERT( m_SwizzleCount < SWIZZLE_PATTERN_MAX ); ++binary; // リンカ用のメタ情報はスキップして値だけ保存する。 for ( int i = 0; i < m_SwizzleCount; i++ ) { m_Swizzle[ i ] = swizzle[ i * 2 ]; } } //--------------------------------------------------------------------------- s32 ShaderBinaryInfo::BuildCommonCommand( u32* bufferAddress, u32 bufferSize ) { SafeBuffer buffer(bufferAddress, bufferSize); // シェーダバイナリの転送コマンドを生成します。 // その他の頂点の設定コマンド等は、ShaderProgramDescription 側に持ちます。 // ジオメトリシェーダのインストラクションの前方部分には、 // 頂点シェーダと同じ命令が格納されているので、ResShaderBinary が // 同じであればシェーダバイナリの再転送は必要ない。 // // この為、ジオメトリシェーダがある場合には必ずジオメトリ分のバイナリも転送する。 if ( this->GetGeometryShaderCount() > 0 ) { this->PutEnableMirroringShaderSetting( buffer, false ); } else { this->PutEnableMirroringShaderSetting( buffer, true ); } this->BuildSwizzleCommand( buffer ); this->BuildProgramCommand( buffer ); return buffer.UsedSize(); } //--------------------------------------------------------------------------- void ShaderBinaryInfo::BuildProgramCommand( SafeBuffer& buffer ) const { // あらかじめ、PutEnableMirroringShaderSetting でミラーリングが有効に // 設定した状態で呼び出される事が前提となっています。 // この関数から抜ける際には、MirroringShaderSetting の値は不定となります。 // まず先頭から512命令以内の部分は、頂点・ジオメトリ共用部分として送信する。 enum { VS_INSTRUCTION_MAX = 512 }; u32 vertexInstructionCount = ut::Min( m_InstructionCount, u32(VS_INSTRUCTION_MAX) ); const u32 VS_COMMAND[] = { 0, internal::MakeCommandHeader( PICA_REG_VS_PROG_ADDR, 1, false, 0xF ) }; buffer.Write( &VS_COMMAND[0], sizeof(VS_COMMAND) ); // 頂点部分のインストラクションのロード。 this->PutLoadCommand( buffer, PICA_REG_VS_PROG_DATA0, &m_pInstruction[ 0 ], vertexInstructionCount ); const u32 VS_RENEWAL_COMMAND[] = { 1, internal::MakeCommandHeader( PICA_REG_VS_PROG_RENEWAL_END, 1, false, 0xF ) }; buffer.Write( &VS_RENEWAL_COMMAND[0], sizeof(VS_RENEWAL_COMMAND) ); if ( this->GetGeometryShaderCount() > 0 ) { const u32 GS_COMMAND[] = { 0, internal::MakeCommandHeader( PICA_REG_GS_PROG_ADDR, 1, false, 0xF ) }; buffer.Write( &GS_COMMAND[0], sizeof(GS_COMMAND) ); // 頂点部分のインストラクションのロード。 this->PutLoadCommand( buffer, PICA_REG_GS_PROG_DATA0, &m_pInstruction[ 0 ], m_InstructionCount ); const u32 GS_RENEWAL_COMMAND[] = { 1, internal::MakeCommandHeader( PICA_REG_GS_PROG_RENEWAL_END, 1, false, 0xF ) }; buffer.Write( &GS_RENEWAL_COMMAND[0], sizeof(GS_RENEWAL_COMMAND) ); } } //--------------------------------------------------------------------------- void ShaderBinaryInfo::BuildSwizzleCommand( SafeBuffer& buffer ) const { // あらかじめ、PutEnableMirroringShaderSetting でミラーリングが有効に // 設定した状態で呼び出される事が前提となっています。 const u32 COMMAND[] = { 0, internal::MakeCommandHeader( PICA_REG_VS_PROG_SWIZZLE_ADDR, 1, false, 0xF ) }; buffer.Write( &COMMAND[0], sizeof(COMMAND) ); NW_ASSERT( m_SwizzleCount > 0 ); // Swizzleパターンのロード this->PutLoadCommand( buffer, PICA_REG_VS_PROG_SWIZZLE_DATA0, &m_Swizzle[0], m_SwizzleCount ); if ( this->GetGeometryShaderCount() > 0 ) { const u32 GS_COMMAND[] = { 0, internal::MakeCommandHeader( PICA_REG_GS_PROG_SWIZZLE_ADDR, 1, false, 0xF ) }; buffer.Write( &GS_COMMAND[0], sizeof(GS_COMMAND) ); NW_ASSERT( m_SwizzleCount > 0 ); // Swizzleパターンのロード this->PutLoadCommand( buffer, PICA_REG_GS_PROG_SWIZZLE_DATA0, &m_Swizzle[0], m_SwizzleCount ); } } //--------------------------------------------------------------------------- s32 ShaderBinaryInfo::BuildShaderProgramCommand( s32 vertexIndex, s32 geometryIndex, u32* bufferAddress, u32 bufferSize ) { SafeBuffer buffer(bufferAddress, bufferSize); NW_ASSERT( ! this->IsGeometryShader( vertexIndex ) ); NW_ASSERT( 0 <= vertexIndex && vertexIndex < this->GetShaderCount() ); NW_ASSERT( geometryIndex < 0 || this->IsGeometryShader( geometryIndex ) ); NW_ASSERT( geometryIndex < this->GetShaderCount() ); if ( geometryIndex < 0 ) { this->PutEnableMirroringShaderSetting( buffer, true ); } else { this->PutEnableMirroringShaderSetting( buffer, false ); } this->BuildConstRegCommand( buffer, vertexIndex ); if ( geometryIndex >= 0 ) { this->BuildConstRegCommand( buffer, geometryIndex ); } this->BuildOutAttrCommand( buffer, vertexIndex, geometryIndex ); return buffer.UsedSize(); } //--------------------------------------------------------------------------- void ShaderBinaryInfo::BuildPrepareCommand( SafeBuffer& buffer ) const { NW_UNUSED_VARIABLE(buffer); // tugal では次のコマンドを生成している。 // ・ジオメトリシェーダの有効化とパイプライン掃除用のダミーコマンド。 // ・0x25e[8:9] へのプリミティブ形状設定。 // ・0x244 への頂点・ジオメトリ設定のミラーリング設定。 // NW では、これらは必要な場合のみ設定するようにする。 } //--------------------------------------------------------------------------- void ShaderBinaryInfo::BuildConstRegCommand( SafeBuffer& buffer, s32 shaderIndex ) const { bool isGeometry = this->IsGeometryShader( shaderIndex ); u32 regFloat = PICA_REG_VS_FLOAT_ADDR; // 0x2c0 u32 regInteger = PICA_REG_VS_INT0; // 0x2b1 if ( isGeometry ) { regFloat = PICA_REG_GS_FLOAT_ADDR; // 0x290 regInteger = PICA_REG_GS_INT0; // 0x281 } // プログラム情報 const ExeImageInfo* exeInfo = m_ExeImageInfo[ shaderIndex ]; // 定数レジスタ情報 struct SetupInfo { u16 type; u16 index; u32 value[4]; }; enum { TYPE_BOOL = 0, TYPE_INT = 1, TYPE_FLOAT = 2 }; const SetupInfo* setupInfoTable = static_cast( ut::AddOffsetToPtr( exeInfo, exeInfo->setupOffset) ); // 定数レジスタのコマンド生成 for ( int i = 0; i < exeInfo->setupCount; ++i ) { const SetupInfo& info = setupInfoTable[ i ]; const u32* value = info.value; switch ( info.type ) { case TYPE_BOOL: break; case TYPE_INT: { const u32 COMMAND[] = { value[ 0 ], internal::MakeCommandHeader(regInteger + info.index, 1, false, 0xF) }; buffer.Write( &COMMAND[0], sizeof(COMMAND) ); } break; case TYPE_FLOAT: { const u32 COMMAND[] = { info.index, // 24 bit モード internal::MakeCommandHeader( regFloat, 4, true, 0xF ), ( value[ 3 ] << 8 & 0xffffff00 ) | ( value[ 2 ] >> 16 & 0x000000ff ), ( value[ 2 ] << 16 & 0xffff0000 ) | ( value[ 1 ] >> 8 & 0x0000ffff ), ( value[ 1 ] << 24 & 0xff000000 ) | ( value[ 0 ] >> 0 & 0x00ffffff ), 0, // padding }; buffer.Write( &COMMAND[0], sizeof(COMMAND) ); } break; } } } //--------------------------------------------------------------------------- void ShaderBinaryInfo::BuildOutAttrCommand( SafeBuffer& buffer, s32 vertexIndex, s32 geometryIndex ) const { bool hasGeometry = geometryIndex >= 0; u32 vertexOutputMask; u32 vertexOutputNum; u32 shaderOutputMask; u32 shaderOutputNum; u32 shaderOutputMap[7]; u32 clockControl = 0x01030703; // クロック制御はひとまず全て有効。 u32 vertexEntry; u32 geometryInputNum; u32 geometryEntry; bool isTextureOutput = false; this->GetOutputRegisterNum( vertexIndex, &vertexOutputNum, &vertexOutputMask ); this->GetShaderOutputRegisterNum( vertexIndex, geometryIndex, &shaderOutputNum, &shaderOutputMask ); this->GetShaderOutputRegisterMap( vertexIndex, geometryIndex, &shaderOutputMap[0] ); vertexEntry = this->GetEntryAddress( vertexIndex ); u32 GEOMETRY_SETTING_COMMAND[] = { 0x00000000, 0x000f0252, 0x00000000, 0x00010254, 0x00000000, 0x00080229, 0x00000000, 0x00020289, }; if ( hasGeometry ) { enum { IDX_REG_252 = 0, IDX_REG_254 = 2, IDX_REG_229 = 4, IDX_REG_289 = 6 }; geometryInputNum = vertexOutputNum; geometryEntry = this->GetEntryAddress( geometryIndex ); u32 geometryMode = this->GetGeometryDataMode( geometryIndex ); GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= geometryMode & 0x3; switch ( geometryMode ) { case 0: // normal mode break; case 1: // subdivision mode { GEOMETRY_SETTING_COMMAND[ IDX_REG_229 ] |= 1u << 31; GEOMETRY_SETTING_COMMAND[ IDX_REG_289 ] |= 1 << 8; u32 geomMainVertexNum = this->GetGeometryMainVertexNum( geometryIndex ); GEOMETRY_SETTING_COMMAND[ IDX_REG_254 ] |= geomMainVertexNum; } break; case 2: // constant data mode { GEOMETRY_SETTING_COMMAND[ IDX_REG_289 ] |= 1 << 8; u32 geomPatchSize = this->GetGeometryPatchSize( geometryIndex ); u32 geomStartIndex = this->GetGeometryStartIndex( geometryIndex ); GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= (geomPatchSize - 1) << 8; GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= (vertexOutputNum - 1) << 12; GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= geomStartIndex << 16; GEOMETRY_SETTING_COMMAND[ IDX_REG_252 ] |= 1 << 24; } break; default: NW_FATAL_ERROR("Invalid geometry mode"); } } // 頂点入出力レジスタ設定 const u32 SHADER_PROGRAM_COMMAND[] = { vertexOutputMask, 0x000f02bd, // 頂点出力マスク (vtx) vertexOutputNum - 1, 0x000f0251, // 出力レジスタ数 - 1 (vtx) vertexOutputNum - 1, 0x000f024a, // 出力レジスタ数 - 1 (vtx) shaderOutputNum - 1, 0x0001025e, // 出力レジスタ数 - 1 (vtx/geom) shaderOutputNum, 0x000f004f, // 出力レジスタ数 (vtx/geom) shaderOutputMap[0], 0x806f0050, // 出力レジスタ0のフォーマット shaderOutputMap[1], shaderOutputMap[2], // 出力レジスタ1,2のフォーマット shaderOutputMap[3], shaderOutputMap[4], // 出力レジスタ3,4のフォーマット shaderOutputMap[5], shaderOutputMap[6], // 出力レジスタ5,6のフォーマット 0x00000001, 0x000f0064, // テクスチャ座標が出力される場合は1 clockControl, 0x000f006f, // シェーダクロック制御 0x7fff0000 | vertexEntry, 0x000f02ba, // 頂点シェーダ開始アドレス }; u32 geometrySettingCommandSize = sizeof(GEOMETRY_SETTING_COMMAND) - sizeof(u32) * (hasGeometry ? 0 : 2); buffer.Write( &GEOMETRY_SETTING_COMMAND[0], geometrySettingCommandSize ); buffer.Write( &SHADER_PROGRAM_COMMAND[0], sizeof(SHADER_PROGRAM_COMMAND) ); if ( hasGeometry ) { // ジオメトリ入出力レジスタ設定 const u32 GEOMETRY_COMMAND[] = { shaderOutputMask, 0x000f028d, // ジオメトリ出力マスク 0x08000000 | (geometryInputNum - 1), 0x00090289, // ジオメトリシェーダの入力レジスタ数 - 1 0x7fff0000 | geometryEntry, 0x000f028a, // ジオメトリシェーダの開始アドレス }; buffer.Write( &GEOMETRY_COMMAND[0], sizeof(GEOMETRY_COMMAND) ); } } //--------------------------------------------------------------------------- void ShaderBinaryInfo::PutEnableMirroringShaderSetting(SafeBuffer& buffer, bool enableMirroring) const { const u32 COMMAND[] = { enableMirroring ? 0 : 1, internal::MakeCommandHeader( PICA_REG_VS_COM_MODE, 1, false, 0x1 ) }; buffer.Write( &COMMAND[0], sizeof(COMMAND) ); } //--------------------------------------------------------------------------- void ShaderBinaryInfo::PutLoadCommand( SafeBuffer& buffer, u32 regAddr, const u32* src, u32 count ) const { enum { WRITE_MAX = 128 }; u32 restCount = count; u32 index = 0; while ( true ) { u32 countPerCommand = ut::Min( restCount, u32(WRITE_MAX) ); const u32 COMMAND[] = { src[index], internal::MakeCommandHeader( regAddr, countPerCommand, false, 0xF ) }; buffer.Write( &COMMAND[0], sizeof(COMMAND) ); buffer.Write( &src[index + 1], (countPerCommand - 1) * sizeof(u32) ); if ( (countPerCommand % 2) == 0 ) { buffer.Write( 0 ); // パディング挿入 } index += countPerCommand; restCount -= countPerCommand; if (restCount == 0) { break; } } } } // namespace gfx } // namespace nw