/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ActivateCommand.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: 27125 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include namespace nw { namespace gfx { nw::os::IAllocator* CommandCacheManager::s_Allocator = NULL; namespace internal { namespace { // 全ロードアレイをクリアし、固定頂点属性を設定する。 const u32 CLEAR_VERTEX_COMMAND[] = { // 固定頂点属性の設定 0xBFFF0000, internal::MakeCommandHeader(0x202, 1, false, 0xF), // ロードアレイの無効化 0, internal::MakeCommandHeader(0x205 , 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 , 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 2, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 3, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 4, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 5, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 6, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 7, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 8, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 9, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 10, 1, false, 0xF), 0, internal::MakeCommandHeader(0x205 + 3 * 11, 1, false, 0xF), 1, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 2, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 3, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 4, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 5, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 6, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 7, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 8, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 9, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 10, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0, 11, internal::MakeCommandHeader(0x232, 4, true, 0xF), (ut::Float24::Float32ToBits24( 1.0f ) << 8), 0, 0, 0 }; } // namespace //--------------------------------------------------------------------------- void ClearVertexAttribute() { NWUseCmdlist( CLEAR_VERTEX_COMMAND, sizeof(CLEAR_VERTEX_COMMAND) ); } //--------------------------------------------------------------------------- template static void SetupActivateVertexAttributeCommand_( TShape shape, ResShaderProgramDescription shaderProgramDesc ) { enum { MAX_ATTRIBUTES_NUM = 12, REG_VTX_SHADER_ATTR_NUM = 0x2b9, // [3:0] 頂点属性数 - 1 REG_VTX_SHADER_ATTR_NUM_2 = 0x242, // [3:0] 頂点族整数 - 1 REG_VTX_MAP_0 = 0x2bb, // [31:0] 入力レジスタのインデックス REG_VTX_MAP_1 = 0x2bc, // [15:0] 入力レジスタのインデックス REG_VTX_STREAM_BASE = 0x200, // [28:1] 頂点アレイのベースアドレス REG_VTX_ARRAY_OFFSET = 0x203, // [27:0] ロードアレイ0のアドレスオフセット REG_VTX_PARAM_INDEX = 0x232, // [3:0] 頂点シェーダの入力番号指定 REG_GEOM_MAP_0 = 0x28b, // [31:0] 入力レジスタのインデックス REG_GEOM_MAP_1 = 0x28c // [15:0] 入力レジスタのインデックス }; const ShaderBinaryInfo* shaderInfo = shaderProgramDesc.GetShaderBinaryInfo(); NW_NULL_ASSERT( shaderInfo ); u32 shaderVtxAttrNum = shaderInfo->GetInputRegisterNum( shaderProgramDesc.GetVertexShaderIndex() ); // VRAM, FCRAM のメモリマップの中からアドレスの一番小さいものをベースに設定 u32 baseAddr = nngxGetPhysicalAddr(nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMA)); shape.ref().m_BaseAddress = baseAddr; s32 vtxAttrNum = shape.GetVertexAttributesCount(); s32 inputRegNum = shaderVtxAttrNum; u32* command = static_cast( NWGetCurrentCmdBuffer() ); // シェーダの入力レジスタ数を設定。 command[0] = (inputRegNum - 1) | 0xa0000000; command[1] = internal::MakeCommandHeader(REG_VTX_SHADER_ATTR_NUM, 1, false, 0xb); command[2] = (inputRegNum - 1); command[3] = internal::MakeCommandHeader(REG_VTX_SHADER_ATTR_NUM_2, 1, false, 0x1); command[4] = 0; command[5] = internal::MakeCommandHeader(REG_VTX_MAP_0, 2, true, 0xF); command[6] = 0; command[7] = 0; command[8] = baseAddr >> 3; command[9] = internal::MakeCommandHeader(REG_VTX_STREAM_BASE, 3, true, 0xF); command[10] = 0; command[11] = static_cast(inputRegNum - 1) << 28; u32* inputMapTable = &command[4]; // 0x2bb u32* inputFormat = &command[10]; // 0x201 u32* vertexParamMask = &command[11]; // 0x202 int inputIndex = 0; // 内部頂点属性番号 int arrayIndex = 0; // ロードアレイ番号 u32 commandIndex = 12; u32 usedFlag = 0; // 0x2bb, 0x2bc に頂点ストリームを前から順に詰めて設定する。 // その後に、固定頂点属性を設定する。未使用の項目は Deactivate 用のコマンドであらかじめクリアされている前提とする。 // 頂点ストリームを番号の若い順に設定 for ( s32 i = 0; i < vtxAttrNum; ++ i ) { ResVertexAttribute attribute = shape.GetVertexAttributes( i ); if ( attribute.GetFlags() & ResVertexAttributeData::FLAG_VERTEX_PARAM ) { // 固定頂点属性は無視する。 continue; } if (attribute.GetFlags() & ResVertexAttributeData::FLAG_INTERLEAVE) // インターリーブフォーマットの場合。 { ResInterleavedVertexStream interleave = ResStaticCast(attribute); // ロードアレイの設定。 // インターリブ形式なので 1 本のロードアレイに複数の要素が入ります。 u32 bufferAddr = nngxGetPhysicalAddr( interleave.GetImageAddress() ); s32 streamCount = interleave.GetVertexStreamsCount(); command[commandIndex ] = bufferAddr - baseAddr; command[commandIndex + 1] = internal::MakeCommandHeader(REG_VTX_ARRAY_OFFSET + 3 * arrayIndex, 3, true, 0xF); command[commandIndex + 2] = 0; command[commandIndex + 3] = (interleave.GetStride() << 16) | (streamCount << 28); u32* arraySetting = &command[commandIndex + 2]; commandIndex += 4; for ( s32 streamIdx = 0; streamIdx < streamCount; ++streamIdx ) { ResVertexStream stream = interleave.GetVertexStreams( streamIdx ); s32 usage = stream.GetUsage(); NW_ASSERT(0 <= usage && usage < MAX_ATTRIBUTES_NUM); inputMapTable[ (inputIndex / 8) * 2 ] |= (usage & 0xf) << (4 * (inputIndex % 8)); // (inputIndex >= 8) ? 2 : 0 のアドレスに書き込む。 usedFlag |= 0x1 << usage; u32 format = stream.GetFormatType(); u32 dimension = stream.GetDimension(); inputFormat[ inputIndex / 8 ] |= CommandCacheHelper::GetVertexFormat(dimension, format) << ((inputIndex % 8) * 4); if (streamIdx < 8) { arraySetting[0] |= inputIndex << (streamIdx * 4); } else { arraySetting[1] |= inputIndex << ((streamIdx - 8) * 4); } ++inputIndex; } } else // 非インターリーブフォーマットの場合。 { // TODO: 今のところ、Usage == 入力レジスタ番号になっているのでそのまま利用。 // シェーダバイナリのフォーマットが公開されるとそちらを使った方が良い。 s32 usage = attribute.GetUsage(); NW_ASSERT(0 <= usage && usage < MAX_ATTRIBUTES_NUM); inputMapTable[ (inputIndex / 8) * 2 ] |= (usage & 0xF) << (4 * (inputIndex % 8)); usedFlag |= 0x1 << usage; ResVertexStream stream = ResStaticCast(attribute); u32 format = stream.GetFormatType(); u32 dimension = stream.GetDimension(); inputFormat[ inputIndex / 8 ] |= CommandCacheHelper::GetVertexFormat(dimension, format) << ((inputIndex % 8) * 4); // ロードアレイの設定 u32 bufferAddr = nngxGetPhysicalAddr( stream.GetImageAddress() ); // このクラスでは interleave 形式には対応しないので、(0番目の要素 == 内部頂点属性 index 番目) command[commandIndex] = bufferAddr - baseAddr; command[commandIndex + 1] = internal::MakeCommandHeader(REG_VTX_ARRAY_OFFSET + 3 * arrayIndex, 3, true, 0xF); command[commandIndex + 2] = inputIndex; command[commandIndex + 3] = (CommandCacheHelper::GetVertexSize(dimension, format) << 16) + (1 << 28); ++inputIndex; commandIndex += 4; } ++arrayIndex; } const u32 HEADER_VTX_PARAM_INDEX = internal::MakeCommandHeader(REG_VTX_PARAM_INDEX, 4, true, 0xF); // 頂点ストリームの後に頂点パラメータを設定。 for ( s32 i = 0; i < vtxAttrNum; ++ i ) { ResVertexAttribute attribute = shape.GetVertexAttributes( i ); if ( attribute.GetFlags() & ResVertexAttributeData::FLAG_VERTEX_PARAM ) { ResVertexParamAttribute param = ResStaticCast(attribute); s32 usage = param.GetUsage(); NW_ASSERT(0 <= usage && usage < 12); inputMapTable[ (inputIndex / 8) * 2 ] |= (usage & 0xF) << (4 * (inputIndex % 8)); usedFlag |= 0x1 << usage; // 頂点パラメータの場合の処理 u32 data[4] = { 0, 0, 0, 0 }; int count = param.GetAttributeCount(); f32* fdata = param.GetAttribute(); for (int j = 0; j < count; ++j) { data[j] = ut::Float24::Float32ToBits24( fdata[j] ); } command[commandIndex] = inputIndex; command[commandIndex + 1] = HEADER_VTX_PARAM_INDEX; command[commandIndex + 2] = (data[3] << 8) | (data[2] >> 16); command[commandIndex + 3] = (data[2] << 16) | (data[1] >> 8); command[commandIndex + 4] = (data[1] << 24) | (data[0]); command[commandIndex + 5] = 0; vertexParamMask[0] |= 1 << (16 + inputIndex); ++inputIndex; commandIndex += 6; } } // 残りの部分には元々固定頂点属性が設定されているのでフラグだけ立てておく。 while ( inputIndex < shaderVtxAttrNum ) { s32 usage = shaderVtxAttrNum - 1; while (usedFlag & (0x1 << usage)) { --usage; } inputMapTable[ (inputIndex / 8) * 2 ] |= (usage & 0xF) << (4 * (inputIndex % 8)); usedFlag |= 0x1 << usage; #if 0 // 不具合発生時の確認用コード // 残りの部分には (0,0,0,1) の固定属性を設定 command[commandIndex] = inputIndex; command[commandIndex + 1] = HEADER_VTX_PARAM_INDEX; command[commandIndex + 2] = (ut::Float24::Float32ToBits24( 1.0f ) << 8); command[commandIndex + 3] = 0; command[commandIndex + 4] = 0; command[commandIndex + 5] = 0; commandIndex += 6; #endif vertexParamMask[0] |= 1 << (16 + inputIndex); ++inputIndex; } const u32 HEADER_GEOM_MAP_0 = internal::MakeCommandHeader(REG_GEOM_MAP_0, 1, false, 0xF); const u32 HEADER_GEOM_MAP_1 = internal::MakeCommandHeader(REG_GEOM_MAP_1, 1, false, 0xF); if (shaderProgramDesc.GetGeometryShaderIndex() >= 0) { // TODO: ジオメトリシェーダの入力マップは、ひとまず 0x76543210, 0xfedcba98 に固定 command[commandIndex + 0] = 0x76543210; command[commandIndex + 1] = HEADER_GEOM_MAP_0; command[commandIndex + 2] = 0xfedcba98; command[commandIndex + 3] = HEADER_GEOM_MAP_1; commandIndex += 4; } NWForwardCurrentCmdBuffer( commandIndex * sizeof(u32) ); } //--------------------------------------------------------------------------- template static void SetupDeactivateVertexAttributeCommand_( TShape shape, ResShaderProgramDescription shaderProgramDesc ) { NW_UNUSED_VARIABLE(shaderProgramDesc); // NOTE: // ・使用したロードアレイの要素数を 0 にリセットする。 // ・使用した頂点の設定を固定頂点属性 (0,0,0,0) にクリアする。 enum { REG_VTX_ARRAY_VTXMASK = 0x202, REG_VTX_ARRAY_OFFSET = 0x203, // [27:0] ロードアレイ0のアドレスオフセット REG_VTX_PARAM_INDEX = 0x232, // [3:0] 頂点シェーダの入力番号指定。 REG_VTX_MAP_0 = 0x2bb, // [31:0] 入力レジスタのインデックス REG_VTX_MAP_1 = 0x2bc // [15:0] 入力レジスタのインデックス }; s32 vtxAttrNum = shape.GetVertexAttributesCount(); u32* command = static_cast( NWGetCurrentCmdBuffer() ); int inputIndex = 0; // 内部頂点属性番号 int arrayIndex = 0; // ロードアレイ番号 u32 commandIndex = 0; // 頂点ストリームを番号の若い順に設定 for ( s32 i = 0; i < vtxAttrNum; ++ i ) { ResVertexAttribute attribute = shape.GetVertexAttributes( i ); // 固定頂点属性は後の処理で無効化する。 if ( attribute.GetFlags() & ResVertexAttributeData::FLAG_VERTEX_PARAM ) { inputIndex++; continue; } if (attribute.GetFlags() & ResVertexAttributeData::FLAG_INTERLEAVE) { ResInterleavedVertexStream interleave = ResStaticCast(attribute); // ロードアレイの無効化 command[commandIndex ] = 0; command[commandIndex + 1] = internal::MakeCommandHeader(REG_VTX_ARRAY_OFFSET + 2 + 3 * arrayIndex, 1, false, 0xF); commandIndex += 2; s32 streamCount = interleave.GetVertexStreamsCount(); inputIndex += streamCount; } else { // ロードアレイの無効化 command[commandIndex ] = 0; command[commandIndex + 1] = internal::MakeCommandHeader(REG_VTX_ARRAY_OFFSET + 2 + 3 * arrayIndex, 1, false, 0xF); commandIndex += 2; ++inputIndex; } ++arrayIndex; } // 入力レジスタマップを初期化 // HACK: ここで設定していなくても正しく表示されるかもしれません。 { const u32 INPUT_MAP0 = 0x76543210; const u32 INPUT_MAP1 = 0x0000ba98; command[commandIndex++] = INPUT_MAP0; command[commandIndex++] = internal::MakeCommandHeader(REG_VTX_MAP_0, 2, true, 0xF); command[commandIndex++] = INPUT_MAP1; command[commandIndex++] = 0; } command[commandIndex++] = (static_cast(inputIndex - 1) << 28) | (((0x1 << inputIndex) - 2) << 16); command[commandIndex++] = internal::MakeCommandHeader(REG_VTX_ARRAY_VTXMASK, 1, false, 0xF); const u32 HEADER_VTX_PARAM_INDEX = internal::MakeCommandHeader(REG_VTX_PARAM_INDEX, 4, true, 0xF); // (0,0,0,0) の固定属性を設定 for ( int i = 1; i < inputIndex; ++i ) { command[commandIndex] = i; command[commandIndex + 1] = HEADER_VTX_PARAM_INDEX; command[commandIndex + 2] = (ut::Float24::Float32ToBits24( 1.0f ) << 8); command[commandIndex + 3] = 0; command[commandIndex + 4] = 0; command[commandIndex + 5] = 0; commandIndex += 6; } NWForwardCurrentCmdBuffer( commandIndex * sizeof(u32) ); } //-------------------------------------------------------------------------- void SetupVertexAttributeCommand( ResSeparateDataShape shape, ResShaderProgramDescription shaderProgramDesc ) { SetupActivateVertexAttributeCommand_( shape, shaderProgramDesc ); } //-------------------------------------------------------------------------- void SetupVertexAttributeCommand( ResParticleShape shape, ResShaderProgramDescription shaderProgramDesc ) { SetupActivateVertexAttributeCommand_( shape, shaderProgramDesc ); } //-------------------------------------------------------------------------- void SetupDeactivateVertexAttributeCommand( ResSeparateDataShape shape, ResShaderProgramDescription shaderProgramDesc ) { SetupDeactivateVertexAttributeCommand_( shape, shaderProgramDesc ); } //-------------------------------------------------------------------------- void SetupDeactivateVertexAttributeCommand( ResParticleShape shape, ResShaderProgramDescription shaderProgramDesc ) { SetupDeactivateVertexAttributeCommand_( shape, shaderProgramDesc ); } //-------------------------------------------------------------------------- void SetupShaderProgramMode(bool useGeometry) { // 初回時と 頂点シェーダとジオメトリシェーダの切り替え時のみ、 // 0x229[1:0]のコマンドとダミーコマンドを積む。 { // ジオメトリシェーダのON/OFF切り替え。 // ジオメトリシェーダを使用しない場合には、0x244を0にすることで、 // 以後、0x2b0-0x2df の設定が 0x280-0x2af にミラーコピーされる。 static u32 USE_GEOMETRY_COMMAND[] = { 0x00000000, 0x00900251, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01d00200, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0, 0x00010229, // index : 44, 45 0x00000000, 0x01d00200, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; const u32 IDX_REG_229 = 44; if ( useGeometry ) { USE_GEOMETRY_COMMAND[ IDX_REG_229 ] = 0x00000002; } else { USE_GEOMETRY_COMMAND[ IDX_REG_229 ] = 0x0; } internal::NWUseCmdlist( &USE_GEOMETRY_COMMAND[0] ); } } //-------------------------------------------------------------------------- void SetupDrawElementsCommand(const ResShaderProgramDescription shaderProgram, ResIndexStream indexStream) { NW_UNUSED_VARIABLE(shaderProgram); NW_UNUSED_VARIABLE(indexStream); } } // namespace internal } // namespace gfx } // namespace nw