1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: gr_Vertex.cpp 4 5 Copyright (C)2009-2012 Nintendo Co., Ltd. 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 $Rev: 46347 $ 14 *---------------------------------------------------------------------------*/ 15 16 #include <nn/gr/CTR/gr_Vertex.h> 17 18 namespace nn 19 { 20 namespace gr 21 { 22 namespace CTR 23 { 24 25 const uptr scBaseAddr = nngxGetPhysicalAddr( nngxGetVramStartAddr( nn::gx::MEM_VRAMA ) ) >> 3; 26 27 //------------------------------------------------------------------------ 28 MakeDrawCommand(bit32 * command,const IndexStream & index_stream) const29 bit32* Vertex::MakeDrawCommand( bit32* command, const IndexStream& index_stream ) const 30 { 31 bit32 fmt = index_stream.isUnsignedByte ? 0x00000000 : 0x80000000; 32 uptr addr = index_stream.physicalAddr - scBaseAddr * 8; 33 34 // Resets vertex information 35 *command++ = 0x1; 36 // 0x25F 37 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_START_DRAW_FUNC1 ); 38 39 // offset 40 *command++ = fmt | addr; 41 // 0x227 42 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_INDEX_ARRAY_ADDR_OFFSET ); 43 44 // Render vertex count 45 *command++ = index_stream.drawVtxNum; 46 // 0x228 47 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_DRAW_VERTEX_NUM ); 48 *command++ = 0x00000000; 49 // 0x245 50 *command++ = PICA_CMD_HEADER_SINGLE_BE( PICA_REG_START_DRAW_FUNC0, 0x1 ); 51 52 // Render kick 53 *command++ = 0x00000001; 54 // 0x22f 55 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_START_DRAW_ELEMENT ); 56 57 *command++ = 0x00000001; 58 // 0x245 59 *command++ = PICA_CMD_HEADER_SINGLE_BE( PICA_REG_START_DRAW_FUNC0, 0x1 ); 60 61 // Flush vertex cache 62 *command++ = 0x00000001; 63 // 0x231 64 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_VERTEX_FUNC ); 65 66 // Send dummy command twice 67 for ( u32 index = 0; index < 2; index++ ) 68 { 69 *command++ = 0x0; 70 // 0x25E 71 *command++ = PICA_CMD_HEADER_SINGLE_BE( PICA_REG_DRAW_MODE2, 0x8 ); 72 } 73 74 return command; 75 } 76 77 //------------------------------------------------------------------------ 78 EnableAttrAsArray(const BindSymbolVSInput & symbol,const uptr physical_addr,const PicaDataVertexAttrType type)79 void Vertex::EnableAttrAsArray( const BindSymbolVSInput& symbol, 80 const uptr physical_addr, 81 const PicaDataVertexAttrType type ) 82 { 83 const bit32 bind_reg = symbol.start; 84 const u32 byte = PicaDataVertexAttrTypeToByteSize( type ); 85 NN_GR_ASSERT( bind_reg < VERTEX_ATTRIBUTE_MAX ); 86 87 // Disabled because it may be set somewhere else 88 DisableAttr_( bind_reg ); 89 90 // Search for an empty stream 91 LoadArray* array = NULL; 92 for ( int i = 0; i < VERTEX_ATTRIBUTE_MAX; ++i ) 93 { 94 if ( m_LoadArray[ i ].IsEnable() == false ) 95 { 96 array = &m_LoadArray[ i ]; 97 break; 98 } 99 } 100 NN_GR_ASSERT( array != NULL ); 101 102 m_IsEnableReg[ bind_reg ] = true; 103 array->physicalAddr = physical_addr; 104 array->type[ 0 ] = type; 105 array->bind[ 0 ] = bind_reg; 106 array->byte[ 0 ] = byte; 107 108 for ( int i = 1; i < VERTEX_ATTRIBUTE_MAX; ++i ) 109 { 110 array->bind[ i ] = -1; 111 array->byte[ i ] = 0; 112 } 113 114 // Disable cache 115 m_CmdCacheVertexNum = 0; 116 } 117 118 //------------------------------------------------------------------------ 119 EnableAttrAsConst(const BindSymbolVSInput & symbol,const u8 dimension,const f32 param[])120 void Vertex::EnableAttrAsConst( const BindSymbolVSInput& symbol, 121 const u8 dimension, 122 const f32 param[] ) 123 { 124 const bit32 bind_reg = symbol.start; 125 126 NN_GR_ASSERT( bind_reg < VERTEX_ATTRIBUTE_MAX ); 127 128 // Disabled because it may be set somewhere else 129 DisableAttr_( bind_reg ); 130 131 m_IsEnableReg[ bind_reg ] = true; 132 m_AttrConst[ bind_reg ].dimension = dimension; 133 134 f32 defaultParam[ 4 ] = { 0.f, 0.f, 0.f, 1.f }; 135 for ( int i = 0; i < 4; ++i ) 136 { 137 m_AttrConst[ bind_reg ].param[ i ] = i < dimension ? param[ i ] : defaultParam[ i ]; 138 } 139 140 // Disable cache 141 m_CmdCacheVertexNum = 0; 142 } 143 144 //------------------------------------------------------------------------ 145 EnableInterleavedArray(const nn::gr::CTR::Vertex::InterleaveInfo & interleave_info,const uptr physical_addr)146 void Vertex::EnableInterleavedArray( const nn::gr::CTR::Vertex::InterleaveInfo& interleave_info, const uptr physical_addr ) 147 { 148 // Search for an empty stream 149 LoadArray* array = NULL; 150 for ( int i = 0; i < VERTEX_ATTRIBUTE_MAX; ++i ) 151 { 152 if ( m_LoadArray[ i ].IsEnable() == false ) 153 { 154 array = &m_LoadArray[ i ]; 155 break; 156 } 157 } 158 NN_GR_ASSERT( array != NULL ); 159 160 for ( int i = 0; i < VERTEX_ATTRIBUTE_MAX; ++i ) 161 { 162 if ( interleave_info.symbol[ i ] == NULL ) 163 { 164 array->bind[ i ] = -1; 165 } 166 else 167 { 168 array->bind[ i ] = interleave_info.symbol[ i ]->start; 169 array->physicalAddr = physical_addr; 170 } 171 172 array->type[ i ] = interleave_info.dataType[ i ]; 173 174 array->byte[ i ] = i < interleave_info.dataNum ? PicaDataVertexAttrTypeToByteSize( array->type[ i ] ) : 0; 175 } 176 177 // Disable cache 178 m_CmdCacheVertexNum = 0; 179 } 180 181 //------------------------------------------------------------------------ 182 DisableAttr_(const bit32 bind_reg)183 void Vertex::DisableAttr_( const bit32 bind_reg ) 184 { 185 NN_GR_ASSERT( bind_reg < VERTEX_ATTRIBUTE_MAX ); 186 187 // Do nothing if already disabled 188 if ( !m_IsEnableReg[ bind_reg ] ) return; 189 190 // Disable cache 191 m_CmdCacheVertexNum = 0; 192 193 if ( m_AttrConst[ bind_reg ].IsEnable() ) 194 { 195 // Disable fixed vertex attributes 196 m_AttrConst[ bind_reg ].Disable(); 197 198 m_IsEnableReg[ bind_reg ] = false; 199 return; 200 } 201 202 203 // Disable if searching in the load array 204 for ( int i = 0; i < VERTEX_ATTRIBUTE_MAX; ++i ) 205 { 206 if ( ! m_LoadArray[ i ].IsEnable() ) continue; 207 208 for ( int j = 0; j < VERTEX_ATTRIBUTE_MAX; ++j ) 209 { 210 if ( m_LoadArray[ i ].bind[ j ] != bind_reg ) continue; 211 212 m_LoadArray[ i ].bind[ j ] = -1; 213 m_IsEnableReg[ bind_reg ] = false; 214 215 // When disabled, checks if the load array itself can be disabled 216 m_LoadArray[ i ].CheckDisable(); 217 218 return; 219 } 220 } 221 222 NN_GR_ASSERT( m_IsEnableReg[ bind_reg ] == false ); 223 } 224 225 //------------------------------------------------------------------------ 226 CheckDisable()227 void Vertex::LoadArray::CheckDisable() 228 { 229 // Do nothing if already disabled 230 if ( !IsEnable() ) 231 { 232 return; 233 } 234 235 // When enabled, disable if all content is invalid 236 for ( u32 index = 0; index < VERTEX_ATTRIBUTE_MAX; ++index ) 237 { 238 if ( bind[ index ] != -1 ) 239 { 240 return; 241 } 242 } 243 244 physicalAddr = NULL; 245 246 for ( u32 index = 0; index < VERTEX_ATTRIBUTE_MAX; ++index ) 247 { 248 byte[ index ] = 0; 249 } 250 } 251 252 //------------------------------------------------------------------------ 253 DisableAll()254 void Vertex::LoadArray::DisableAll() 255 { 256 physicalAddr = NULL; 257 258 for ( u32 index = 0; index < VERTEX_ATTRIBUTE_MAX; index++ ) 259 { 260 // Initialize with 0x0 261 type[ index ] = PICA_DATA_SIZE_1_BYTE; 262 263 byte[ index ] = 0; 264 265 bind[ index ] = -1; 266 } 267 } 268 269 //------------------------------------------------------------------------ 270 MakeEnableAttrCommand_(bit32 * command) const271 bit32* Vertex::MakeEnableAttrCommand_( bit32* command ) const 272 { 273 // #define NN_GR_VERTEX_DUMP 274 #if defined( NN_GR_VERTEX_DUMP ) // For debugging 275 bit32 * start = command; 276 #endif 277 278 // Set the vertex shader input register count later 279 bit32* reg0x2b9 = command++; // = vtx_attr_num | 0xa0000000; 280 *command++ = 0x000b02b9; 281 282 bit32* reg0x242 = command++; // = vtx_attr_num; 283 *command++ = 0x00010242; 284 285 // Set bind_reg_map 286 bit32* bind_reg_command = command; 287 288 // 0x2bb 289 *command++ = 0; 290 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_VS_ATTR_IN_REG_MAP0 ); 291 292 // 0x2bc 293 *command++ = 0; 294 *command++ = PICA_CMD_HEADER_SINGLE( PICA_REG_VS_ATTR_IN_REG_MAP1 ); 295 296 // 0x201 297 bit32* type = &command[ 2 ]; 298 299 // 0x202[27:16] 300 bit32& fixed_attr_mask = command[ 3 ]; 301 302 // 0x200 303 *command++ = scBaseAddr; 304 bit32* headerBaseAddr = command++; // = PICA_CMD_HEADER_BURSTSEQ( PICA_REG_VERTEX_ATTR_ARRAYS_BASE_ADDR, 3 + 3 * m_AttrArrayNum ); 305 306 // 0x201 307 *command++ = 0; 308 309 // 0x202 310 bit32* reg0x202 = command++; // vtx_attr_num << 28; 311 312 int input_index = 0; 313 int array_num = 0; 314 for ( int i = 0; i < VERTEX_ATTRIBUTE_MAX; ++i ) 315 { 316 const LoadArray& vtx_array = m_LoadArray[ i ]; 317 318 if ( !vtx_array.IsEnable() ) continue; 319 320 ++array_num; 321 int total_byte = 0; 322 int elem_num = 0; 323 int elem[ 2 ] = { 0, 0 }; 324 int maxAlign = 1; 325 326 for ( int j = 0; j < VERTEX_ATTRIBUTE_MAX; ++j ) 327 { 328 // Consider padding automatically inserted depending on the order of all elements type 329 switch (vtx_array.type[ j ]) 330 { 331 case PICA_DATA_SIZE_1_SHORT: 332 case PICA_DATA_SIZE_2_SHORT: 333 case PICA_DATA_SIZE_3_SHORT: 334 case PICA_DATA_SIZE_4_SHORT: 335 total_byte = ((total_byte + 1) & ~0x1); 336 if (maxAlign < 2) { maxAlign = 2; } 337 break; 338 339 case PICA_DATA_SIZE_1_FLOAT: 340 case PICA_DATA_SIZE_2_FLOAT: 341 case PICA_DATA_SIZE_3_FLOAT: 342 case PICA_DATA_SIZE_4_FLOAT: 343 total_byte = ((total_byte + 3) & ~0x3); 344 if (maxAlign < 4) { maxAlign = 4; } 345 break; 346 347 default: 348 break; 349 } 350 351 if ( vtx_array.byte[ j ] == 0 ) break; 352 total_byte += vtx_array.byte[ j ]; 353 354 // When bound 355 if ( vtx_array.bind[ j ] >= 0 ) 356 { 357 // 0x204 + ( 3 * N ) or 0x205 + ( 3 * N ) 358 elem[ j / 8 ] |= input_index << ( 4 * ( j % 8 ) ); 359 360 // 0x201 or 0x202 361 type[ input_index / 8 ] |= vtx_array.type[ j ] << ( 4 * ( input_index % 8 ) ); 362 363 // Set the input register index in 0x2bb or 0x2bc 364 bind_reg_command[ ( input_index >> 3 ) << 1 ] &= ~( 0xf << ( 4 * ( input_index % 8 ) ) ); 365 bind_reg_command[ ( input_index >> 3 ) << 1 ] |= vtx_array.bind[ j ] << ( 4 * ( input_index % 8 ) ); 366 367 ++input_index; 368 369 #if defined( NN_GR_VERTEX_DUMP ) // For debugging 370 NN_LOG( "+ 0x%08x 0x%08x 0x%08x\n", vtx_array.byte[ j ], elem[0], elem[1] ); 371 #endif 372 } 373 // For Padding 374 else 375 { 376 // 0x204 + ( 3 * N ) or 0x205 + ( 3 * N ) 377 elem[ j / 8 ] |= ( ( vtx_array.byte[ j ] >> 2 ) + 0xb ) << ( 4 * ( j % 8 ) ); 378 379 #if defined( NN_GR_VERTEX_DUMP ) // For debugging 380 NN_LOG( "- 0x%08x 0x%08x 0x%08x\n", vtx_array.byte[ j ] >> 2, elem[0], elem[1] ); 381 #endif 382 } 383 384 ++elem_num; 385 } 386 387 // 0x203 + ( 3 * N ) 388 *command++ = vtx_array.physicalAddr - scBaseAddr * 8; 389 390 // 0x204 + ( 3 * N ) 391 *command++ = elem[ 0 ]; 392 393 // 0x205 + ( 3 * N ) 394 // Strides for each vertex is a multiple of the size of the largest type of the vertex attributes included in the vertex data structure 395 // Padding automatically inserted must be considered 396 *command++ = elem[ 1 ] | ((total_byte + (maxAlign - 1)) & ~(maxAlign - 1)) << 16 | elem_num << 28; 397 } 398 399 // 0x200 400 *headerBaseAddr = PICA_CMD_HEADER_BURSTSEQ( PICA_REG_VERTEX_ATTR_ARRAYS_BASE_ADDR, 3 + 3 * array_num ); 401 402 if ( array_num % 2 ) *command++ = 0; 403 404 // Fixed vertex setup 405 for ( int bind_reg = VERTEX_ATTRIBUTE_MAX-1; bind_reg >= 0; --bind_reg ) 406 { 407 const AttrConst& vtxConst = m_AttrConst[ bind_reg ]; 408 409 if ( !vtxConst.IsEnable() ) continue; 410 411 // Index to input to the vertex shader 412 *command++ = input_index; 413 // 0x232 414 *command++ = PICA_CMD_HEADER_BURSTSEQ( PICA_REG_VS_FIXED_ATTR , 4 ); 415 // The 3 data portions of the fixed vertex attributes converted to 24 bit: 0x233, 0x234, 0x235 416 *command++ = ( ( Float32ToFloat24( vtxConst.param[3] ) << 8 ) & 0xffffff00 ) | ( ( Float32ToFloat24( vtxConst.param[2] ) >> 16 ) & 0x000000ff ); 417 *command++ = ( ( Float32ToFloat24( vtxConst.param[2] ) << 16 ) & 0xffff0000 ) | ( ( Float32ToFloat24( vtxConst.param[1] ) >> 8 ) & 0x0000ffff ); 418 *command++ = ( ( Float32ToFloat24( vtxConst.param[1] ) << 24 ) & 0xff000000 ) | ( ( Float32ToFloat24( vtxConst.param[0] ) >> 0 ) & 0x00ffffff ); 419 *command++ = 0; // Padding 420 421 // Set the input register index in 0x2bb or 0x2bc 422 bind_reg_command[ ( input_index / 8 ) * 2 ] &= ~( 0xf << ( 4 * ( input_index % 8 ) ) ); 423 bind_reg_command[ ( input_index / 8 ) * 2 ] |= bind_reg << ( 4 * ( input_index % 8 ) ); 424 425 // 0x202[27:16] Fixed vertex attribute mask 426 fixed_attr_mask |= ( 1 << input_index + 16 ); 427 428 ++input_index; 429 } 430 431 // Vertex attribute input count setting 432 // 0x2b9 433 *reg0x2b9 = (input_index - 1) | 0xa0000000; 434 // 0x242 435 *reg0x242 = input_index - 1; 436 // 0x202 437 *reg0x202 |= (input_index - 1) << 28; 438 439 #if defined( NN_GR_VERTEX_DUMP ) // For debugging 440 static int a = 0; 441 if ( ++a == 1 ) 442 { 443 for ( bit32* i = start; i != command; i +=2 ) 444 { 445 NN_LOG( "0x%08x 0x%08x\n", *i, *(i+1) ); 446 } 447 } 448 #endif 449 450 return command; 451 } 452 453 } // namespace CTR 454 } // namespace gr 455 } // namespace nn 456