/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_ParticleShape.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: 25788 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include namespace nw { namespace gfx { NW_UT_RUNTIME_TYPEINFO_DEFINITION(ParticleShape, SceneObject); static const int MAX_ATTRIBUTES_NUM = 12; //---------------------------------------- static void SetupParticleVertexAttributeCommand( ParticleShape* shape, ParticleSet* particleSet, ParticleBuffer bufferSide) { ParticleCollection* collection = particleSet->GetParticleCollection(); enum { 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] 入力レジスタのインデックス }; u32 baseAddr = nngxGetPhysicalAddr(nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMA)); s32 vtxAttrNum = shape->GetVertexAttributesCount(); u32* command = reinterpret_cast(shape->m_CommandCache[bufferSide]); const u32 HEADER_VTX_SHADER_ATTR_NUM = internal::MakeCommandHeader(REG_VTX_SHADER_ATTR_NUM, 1, false, 0xb); const u32 HEADER_VTX_SHADER_ATTR_NUM_2 = internal::MakeCommandHeader(REG_VTX_SHADER_ATTR_NUM_2, 1, false, 0x1); const u32 HEADER_VTX_MAP_0 = internal::MakeCommandHeader(REG_VTX_MAP_0, 2, true, 0xF); command[0] = (vtxAttrNum - 1) | 0xa0000000; command[1] = HEADER_VTX_SHADER_ATTR_NUM; command[2] = (vtxAttrNum - 1); command[3] = HEADER_VTX_SHADER_ATTR_NUM_2; command[4] = 0; command[5] = HEADER_VTX_MAP_0; command[6] = 0; command[7] = 0; NW_ASSERT((baseAddr & 0x0f) == 0); command[8] = baseAddr >> 3; command[9] = internal::MakeCommandHeader(REG_VTX_STREAM_BASE, 3 + vtxAttrNum * 3, true, 0xF); command[10] = 0; command[11] = static_cast(vtxAttrNum - 1) << 28; u32* inputTable = &command[4]; u32* inputFormat = &command[10]; u32* vertexParamMask = &command[11]; int inputIndex = 0; // 内部頂点属性番号 u32 commandIndex = 12; u32 usedFlag = 0; // 頂点ストリームを番号の若い順に設定 for (s32 i = 0; i < shape->GetVertexAttributesCount(); ++i) { if (shape->IsVertexStream(i)) { s32 usage = shape->GetVertexAttributeUsage(i); NW_ASSERT(0 <= usage && usage < 12); inputTable[ (inputIndex / 8) * 2 ] |= (usage & 0xF) << (4 * (inputIndex % 8)); usedFlag |= 0x1 << usage; u32 format = shape->GetVertexAttributeFormatType(i); u32 dimension = shape->GetVertexAttributeDimension(i); inputFormat[ inputIndex / 8 ] |= internal::CommandCacheHelper::GetVertexFormat(dimension, format) << ((inputIndex % 8) * 4); // ロードアレイの設定 u8* streamPtr = shape->GetVertexStreamPtr(i, bufferSide); NW_NULL_ASSERT(streamPtr); u32 bufferAddr = nngxGetPhysicalAddr(reinterpret_cast(streamPtr)); shape->SetVertexAttributeCommandPtr(i, bufferSide, &command[commandIndex]); // このクラスでは interleave 形式には対応しないので、(0番目の要素 == 内部頂点属性 index 番目) NW_ASSERT((bufferAddr - baseAddr) < 0x10000000); command[commandIndex++] = bufferAddr - baseAddr; command[commandIndex++] = inputIndex; command[commandIndex++] = (internal::CommandCacheHelper::GetVertexSize(dimension, format) << 16) + (1 << 28); ++inputIndex; } } // パラメータの分を設定 for (s32 i = inputIndex; i < vtxAttrNum; ++i) { command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; } // 64bit単位になるようにパディング if ((commandIndex & 1) == 1) { command[commandIndex++] = 0; } // 頂点ストリームの後に頂点パラメータを設定。 for (s32 i = 0; i < shape->GetVertexAttributesCount(); ++i) { if (!shape->IsVertexStream(i)) { s32 usage = shape->GetVertexAttributeUsage(i); NW_ASSERT(0 <= usage && usage < 12); inputTable[ (inputIndex / 8) * 2 ] |= (usage & 0xF) << (4 * (inputIndex % 8)); usedFlag |= 0x1 << usage; // 頂点パラメータの場合の処理 u32 data[4] = { 0, 0, 0, 0 }; int count = shape->GetVertexAttributeDimension(i); f32* fdata = shape->GetVertexParameter(i); for (int j = 0; j < count; ++j) { data[j] = ut::Float24::Float32ToBits24(fdata[j]); } const u32 HEADER_VTX_PARAM_INDEX = internal::MakeCommandHeader(REG_VTX_PARAM_INDEX, 4, true, 0xF); shape->SetVertexAttributeCommandPtr(i, bufferSide, &command[commandIndex + 2]); 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; } } const u32 HEADER_GEOM_MAP_0 = internal::MakeCommandHeader(REG_GEOM_MAP_0, 2, true, 0xF); command[commandIndex + 0] = 0x76543210; command[commandIndex + 1] = HEADER_GEOM_MAP_0; command[commandIndex + 2] = 0xfedcba98; command[commandIndex + 3] = 0; commandIndex += 4; shape->m_CommandCacheSize[bufferSide] = commandIndex * sizeof(u32); } //---------------------------------------- static void SetupDeactivateParticleVertexAttributeCommand( ParticleShape* shape) { enum { REG_VTX_ARRAY_OFFSET = 0x203, // [27:0] ロードアレイ0のアドレスオフセット REG_VTX_PARAM_INDEX = 0x232 // [3:0] 頂点シェーダの入力番号指定 }; s32 vtxAttrNum = shape->GetVertexAttributesCount(); u32* command = reinterpret_cast(shape->m_DeactivateVertexCommandCache); shape->m_DeactivateVertexCommandCache = command; int inputIndex = 0; // 内部頂点属性番号 u32 commandIndex = 0; // 頂点ストリームを番号の若い順に設定 for (s32 i = 0; i < vtxAttrNum; ++i) { if (shape->IsVertexStream(i)) { // ロードアレイの無効化 // このクラスでは interleave 形式には対応しないので、(0番目の要素 == 内部頂点属性 index 番目) command[commandIndex++] = 0; command[commandIndex++] = internal::MakeCommandHeader(REG_VTX_ARRAY_OFFSET + 2 + 3 * inputIndex, 1, false, 0xF); ++inputIndex; } } const u32 HEADER_VTX_PARAM_INDEX = internal::MakeCommandHeader(REG_VTX_PARAM_INDEX, 4, true, 0xF); // (0,0,0,0) の固定属性を設定 for ( int i = 0; i < inputIndex; ++i ) { command[commandIndex++] = i; command[commandIndex++] = HEADER_VTX_PARAM_INDEX; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; } shape->m_DeactivateVertexCommandCacheSize = commandIndex * sizeof(u32); NW_ASSERT( shape->m_DeactivateVertexCommandCacheSize <= sizeof(u32) * (MAX_ATTRIBUTES_NUM * 8) ); } static const int PrimitiveCommandSize = 26; //---------------------------------------- static void CreatePrimitiveCommandCache( ParticleShape* shape, ResParticleSet resParticleSet) { enum { REG_VERTEX_UNIFORM_BOOL = 0x2b0, REG_ELEMENTS_MODE = 0x229, // [23:16]にはバイトイネーブルでアクセスしてはいけない。 REG_ELEMENTS_MODE_2 = 0x253, // [31:16]にはバイトイネーブルでアクセスしてはいけない。 REG_ELEMENTS_MODE_3 = 0x25e, // [31:16]にはバイトイネーブルでアクセスしてはいけない。 REG_TRIANGLE_INDEX_RESET = 0x25f, REG_DRAW_READY = 0x245, REG_DRAW_KICK = 0x22f, REG_VERTEX_CACHE_CLEAR = 0x231, REG_COLOR_DEPTH_CACHE_CLEAR = 0x110, REG_COLOR_DEPTH_CACHE_FLUSH = 0x111 }; u32* command = reinterpret_cast(shape->m_PrimitiveCommandCache); u32 commandIndex = 0; s32 type; switch (resParticleSet.GetParticleShapeBuilder().GetTypeInfo()) { case ResParticleBillboardShapeBuilder::TYPE_INFO: type = 0; break; case ResParticleWorldBillboardShapeBuilder::TYPE_INFO: type = 8; break; case ResParticleYBillboardShapeBuilder::TYPE_INFO: type = 2 | 1; break; case ResParticleXyPlaneShapeBuilder::TYPE_INFO: type = 4 | 1; break; default: NW_FATAL_ERROR("unknown shapebuilder type"); break; } const u32 HEADER_VERTEX_UNIFORM_BOOL = internal::MakeCommandHeader(REG_VERTEX_UNIFORM_BOOL, 5, true, 0xF); const u32 HEADER_ELEMENTS_MODE = internal::MakeCommandHeader(REG_ELEMENTS_MODE, 1, false, 0x2); const u32 HEADER_ELEMENTS_MODE_2 = internal::MakeCommandHeader(REG_ELEMENTS_MODE_2, 1, false, 0x2); const u32 HEADER_ELEMENTS_MODE_3 = internal::MakeCommandHeader(REG_ELEMENTS_MODE_3, 1, false, 0x2); const u32 HEADER_TRIANGLE_INDEX_RESET = internal::MakeCommandHeader(REG_TRIANGLE_INDEX_RESET, 1, false, 0xF); const u32 HEADER_DRAW_READY = internal::MakeCommandHeader(REG_DRAW_READY, 1, false, 0xF); const u32 HEADER_DRAW_KICK = internal::MakeCommandHeader(REG_DRAW_KICK, 1, false, 0xF); const u32 HEADER_VERTEX_CACHE_CLEAR = internal::MakeCommandHeader(REG_VERTEX_CACHE_CLEAR, 1, false, 0xF); const u32 HEADER_COLOR_DEPTH_CACHE_FLUSH = internal::MakeCommandHeader(REG_COLOR_DEPTH_CACHE_FLUSH, 1, false, 0xF); const u32 HEADER_COLOR_DEPTH_CACHE_CLEAR = internal::MakeCommandHeader(REG_COLOR_DEPTH_CACHE_CLEAR, 1, false, 0xF); command[commandIndex++] = 0x7fff0000 | (type << 1); // bit0は予約されているため、bit1から配置する command[commandIndex++] = HEADER_VERTEX_UNIFORM_BOOL; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = 0; command[commandIndex++] = HEADER_ELEMENTS_MODE; command[commandIndex++] = 0; command[commandIndex++] = HEADER_ELEMENTS_MODE_2; command[commandIndex++] = 3 << 8; command[commandIndex++] = HEADER_ELEMENTS_MODE_3; command[commandIndex++] = 1; command[commandIndex++] = HEADER_TRIANGLE_INDEX_RESET; command[commandIndex++] = 0; command[commandIndex++] = HEADER_DRAW_READY; command[commandIndex++] = 1; command[commandIndex++] = HEADER_DRAW_KICK; command[commandIndex++] = 1; command[commandIndex++] = HEADER_DRAW_READY; command[commandIndex++] = 1; command[commandIndex++] = HEADER_VERTEX_CACHE_CLEAR; command[commandIndex++] = 1; command[commandIndex++] = HEADER_COLOR_DEPTH_CACHE_FLUSH; command[commandIndex++] = 1; command[commandIndex++] = HEADER_COLOR_DEPTH_CACHE_CLEAR; NW_ASSERT(commandIndex == PrimitiveCommandSize); shape->m_PrimitiveCommandCacheSize = commandIndex * sizeof(u32); } //---------------------------------------- void ParticleShape::CreateCommandCache( ParticleSet* particleSet ) { for (int side = 0; side < 2; ++side) { SetupParticleVertexAttributeCommand( this, particleSet, (ParticleBuffer)side); } SetupDeactivateParticleVertexAttributeCommand(this); CreatePrimitiveCommandCache( this, particleSet->GetResParticleSet()); } void ParticleShape::GetMemorySizeInternal( os::MemorySizeCalculator* pSize, int capacity) { os::MemorySizeCalculator& size = *pSize; const int streamSize = (capacity + internal::PARTICLE_SIMD_WIDTH_MAX) * sizeof(u16); const int drawCommandCacheSize = sizeof(u32) * (12 + MAX_ATTRIBUTES_NUM * 6 + MAX_ATTRIBUTES_NUM * 4 + 4); const int primitiveCommandCacheSize = sizeof(u32) * PrimitiveCommandSize; const int deactivateVertexCommandCacheSize = sizeof(u32) * (MAX_ATTRIBUTES_NUM * 8); int deviceMemorySize = 0; if (size.GetAlignment() < 32) { deviceMemorySize += 32 - size.GetAlignment(); // 最悪値 } deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; int nodeMemorySize = sizeof(ParticleShape); nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += drawCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += drawCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += deactivateVertexCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += primitiveCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); size.Add(nodeMemorySize, 32); } void ParticleShape::GetDeviceMemorySizeInternal( os::MemorySizeCalculator* pSize, int capacity) { os::MemorySizeCalculator& size = *pSize; const int streamSize = (capacity + internal::PARTICLE_SIMD_WIDTH_MAX) * sizeof(u16); const int drawCommandCacheSize = sizeof(u32) * (12 + MAX_ATTRIBUTES_NUM * 6 + MAX_ATTRIBUTES_NUM * 4 + 4); const int primitiveCommandCacheSize = sizeof(u32) * PrimitiveCommandSize; const int deactivateVertexCommandCacheSize = sizeof(u32) * (MAX_ATTRIBUTES_NUM * 8); int deviceMemorySize = 0; if (size.GetAlignment() < 32) { deviceMemorySize += 32 - size.GetAlignment(); // 最悪値 } deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; size.Add(deviceMemorySize, 32); } //---------------------------------------- ParticleShape* ParticleShape::Create( ResSceneObject resource, int capacity, os::IAllocator* mainAllocator, os::IAllocator* deviceAllocator ) { NW_NULL_ASSERT(mainAllocator); NW_NULL_ASSERT(deviceAllocator); ResParticleShape resNode = ResDynamicCast(resource); NW_ASSERT(resNode.IsValid()); const int streamSize = (capacity + internal::PARTICLE_SIMD_WIDTH_MAX) * sizeof(u16); const int drawCommandCacheSize = sizeof(u32) * (12 + MAX_ATTRIBUTES_NUM * 6 + MAX_ATTRIBUTES_NUM * 4 + 4); const int primitiveCommandCacheSize = sizeof(u32) * PrimitiveCommandSize; const int deactivateVertexCommandCacheSize = sizeof(u32) * (MAX_ATTRIBUTES_NUM * 8); int deviceMemorySize = 0; deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; deviceMemorySize = ut::RoundUp(deviceMemorySize, 32); deviceMemorySize += streamSize; int nodeMemorySize = sizeof(ParticleShape); nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += drawCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += drawCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += deactivateVertexCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); nodeMemorySize += primitiveCommandCacheSize; nodeMemorySize = ut::RoundUp(nodeMemorySize, 32); u8* devicememory = reinterpret_cast(deviceAllocator->Alloc(deviceMemorySize, 32)); if (devicememory == NULL) { return NULL; } u8* nodememory = reinterpret_cast(mainAllocator->Alloc(nodeMemorySize, 32)); if (nodememory == NULL) { deviceAllocator->Free(devicememory); return NULL; } ParticleShape* node = new(nodememory) ParticleShape( capacity, mainAllocator, deviceAllocator, devicememory, resNode); nodememory += sizeof(ParticleShape); for (int i = 0; i < 2; ++i) { devicememory = reinterpret_cast(ut::RoundUp(devicememory, 32)); node->m_PrimitiveBuffer[i] = devicememory; devicememory += streamSize; //std::memset(node->m_PrimitiveBuffer[i], 0, streamSize); u32 baseAddr = nngxGetPhysicalAddr(nn::gx::GetVramStartAddr(nn::gx::MEM_VRAMA)); u32 bufferAddr = nngxGetPhysicalAddr(reinterpret_cast(node->m_PrimitiveBuffer[i])); NW_ASSERT((bufferAddr - baseAddr) < 0x10000000); node->m_PrimitiveBufferOffset[i] = bufferAddr - baseAddr; } for (int side = 0; side < 2; ++side) { nodememory = reinterpret_cast(ut::RoundUp(nodememory, 32)); node->m_CommandCache[side] = reinterpret_cast(nodememory); nodememory += drawCommandCacheSize; } nodememory = reinterpret_cast(ut::RoundUp(nodememory, 32)); node->m_DeactivateVertexCommandCache = reinterpret_cast(nodememory); nodememory += deactivateVertexCommandCacheSize; nodememory = reinterpret_cast(ut::RoundUp(nodememory, 32)); node->m_PrimitiveCommandCache = reinterpret_cast(nodememory); nodememory += primitiveCommandCacheSize; return node; } int ParticleShape::AddVertexStreamSize( u32 formatType, int dimension, int capacity, int prevSize ) { int formatSize = 1; switch (formatType) { case GL_FLOAT: formatSize = 4; break; default: NW_FATAL_ERROR("unsupported formatType"); } int streamSize = (capacity + internal::PARTICLE_SIMD_WIDTH_MAX) * dimension * formatSize; prevSize = ut::RoundUp(prevSize, 32); prevSize += streamSize; prevSize = ut::RoundUp(prevSize, 32); prevSize += streamSize; return prevSize + streamSize; } ParticleShape::VertexAttribute* ParticleShape::AddVertexStream( s32 usage, u32 formatType, int dimension, int capacity, u8** memory ) { int formatSize = 1; switch (formatType) { case GL_FLOAT: formatSize = 4; break; default: NW_FATAL_ERROR("unsupported formatType"); } *memory = reinterpret_cast(ut::RoundUp(*memory, 32)); int streamSize = (capacity + internal::PARTICLE_SIMD_WIDTH_MAX) * dimension * formatSize; void* stream0 = *memory; *memory += streamSize; void* stream1 = *memory; *memory += streamSize; std::memset(stream0, 0, streamSize); std::memset(stream1, 0, streamSize); return this->AddVertexAttribute( usage, formatType, dimension, true, reinterpret_cast(stream0), reinterpret_cast(stream1)); } int ParticleShape::AddVertexParamSize( u32 formatType, int dimension, int prevSize ) { int formatSize = 1; switch (formatType) { case GL_FLOAT: formatSize = 4; break; default: NW_FATAL_ERROR("unsupported formatType"); } prevSize = ut::RoundUp(prevSize, 32); int streamSize = dimension * formatSize; return prevSize + streamSize; } ParticleShape::VertexAttribute* ParticleShape::AddVertexParam( s32 usage, u32 formatType, int dimension, f32* parameters, u8** memory ) { NW_UNUSED_VARIABLE(parameters); int formatSize = 1; switch (formatType) { case GL_FLOAT: formatSize = 4; break; default: NW_FATAL_ERROR("unsupported formatType"); } int streamSize = dimension * formatSize; *memory = reinterpret_cast(ut::RoundUp(*memory, 32)); void* stream = *memory; *memory += streamSize; std::memcpy(stream, parameters, streamSize); return this->AddVertexAttribute( usage, formatType, dimension, false, reinterpret_cast(stream), NULL); } //---------------------------------------- ParticleShape::ParticleShape( int capacity, os::IAllocator* allocator, os::IAllocator* deviceAllocator, void* deviceMemory, ResParticleShape resObj) : SceneObject(allocator, resObj), m_Capacity(capacity), m_BufferSide(false), m_ResVertexAttributeDataCount(0), m_DeviceAllocator(deviceAllocator), m_DeviceMemory(deviceMemory) { m_PrimitiveBuffer[0] = NULL; m_PrimitiveBuffer[1] = NULL; m_PrimitiveBufferOffset[0] = 0; m_PrimitiveBufferOffset[1] = 0; for (int usage = 0; usage < PARTICLEUSAGE_COUNT; ++usage) { for (int side = 0; side < 2; ++side) { m_VertexAttribute[usage].m_Stream[side] = NULL; m_VertexAttribute[usage].m_CommandPtr[side] = NULL; } } for (int i = 0; i < 2; ++i) { m_CommandCache[i] = NULL; m_CommandCacheSize[i] = 0; } m_DeactivateVertexCommandCache = NULL; m_DeactivateVertexCommandCacheSize = 0; m_PrimitiveCommandCache = NULL; m_PrimitiveCommandCacheSize = 0; } //---------------------------------------- ParticleShape::~ParticleShape() { m_DeviceAllocator->Free(m_DeviceMemory); for (int i = 0; i < 2; ++ i) { if (this->m_CommandCache[i] != NULL) { this->m_CommandCache[i] = NULL; this->m_CommandCacheSize[i] = 0; } } if (this->m_DeactivateVertexCommandCache != NULL) { this->m_DeactivateVertexCommandCache = NULL; this->m_DeactivateVertexCommandCacheSize = 0; } if (this->m_PrimitiveCommandCache != NULL) { this->m_PrimitiveCommandCache = NULL; this->m_PrimitiveCommandCacheSize = 0; } } void ParticleShape::FlushBuffer() { for (s32 i = 0; i < this->GetVertexAttributesCount(); ++i) { if (this->IsVertexStream(i)) { nngxUpdateBuffer( this->GetVertexStreamPtr(i, PARTICLE_BUFFER_FRONT), this->GetVertexCapacity() * this->GetVertexAttributeDimension(i) * sizeof(f32)); } } nngxUpdateBuffer( this->GetPrimitiveStreamPtr(PARTICLE_BUFFER_FRONT), this->GetVertexCapacity() * sizeof(u16)); } } // namespace gfx } // namespace nw