嗨
我正在DirectX 11中实现一个粒子系统,并使用Intel AVX instrinsic更新粒子数据,并在将其传递到IA阶段之前将其从SoA(阵列结构(转换为AoS(结构阵列(。
当我在重映射阶段使用AVX intrisincs时,它似乎会导致包含粒子顶点的顶点缓冲区损坏并导致崩溃!
我以SoA的方式构建了粒子数据:
float* mXPosition;
float* mYPosition;
float* mZPosition;
我为每个组件分配联盟内存
mXPosition = (float*) _aligned_malloc( NUM_PARTICLES * sizeof(float), 32 );
mYPosition = (float*) _aligned_malloc( NUM_PARTICLES * sizeof(float), 32 );
mZPosition = (float*) _aligned_malloc( NUM_PARTICLES * sizeof(float), 32 );
我使用D3D11_USAGE_DYNAMIC
和D3D11_CPU_ACCESS_WRITE
创建顶点缓冲区,以便能够在CPU上修改粒子数据。
D3D11_BUFFER_DESC desc;
ZeroMemory( &desc, sizeof( desc ) );
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = sizeof(ParticleVertex12) * NUM_PARTICLES;
desc.StructureByteStride = sizeof(ParticleVertex12);
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
//Allocating aligned memory for array used for maping vertices to buffer
mVertices = (float*) _aligned_malloc( ( NUM_PARTICLES * 3 ) * sizeof(float), 32 );
if( FAILED( device->CreateBuffer( &desc, &subData, &mVertexBuffer ) ) )
return E_FAIL;
顶点缓冲区已成功创建。
重映射阶段
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = deviceContext->Map( mVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
if( SUCCEEDED( hr ) )
{
size_t counter = 0;
for (int baseIndex = 0; baseIndex < NUM_PARTICLES / 8; baseIndex++)
{
// Mapping from SOA-pattern to AOS-pattern
//Load
__m256 xReg = _mm256_load_ps( &mXPosition[baseIndex * 8] );
__m256 yReg = _mm256_load_ps( &mYPosition[baseIndex * 8] );
__m256 zReg = _mm256_load_ps( &mZPosition[baseIndex * 8] );
//Set test values
xReg = _mm256_set_ps( 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f );
yReg = _mm256_set_ps( 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, 27.0f, 28.0f );
zReg = _mm256_set_ps( 31.0f, 32.0f, 33.0f, 34.0f, 35.0f, 36.0f, 37.0f, 38.0f );
//Shuffle
__m256 xyReg = _mm256_shuffle_ps( xReg, yReg, _MM_SHUFFLE( 2,0,2,0 ) );
__m256 yzReg = _mm256_shuffle_ps( yReg, zReg, _MM_SHUFFLE( 3,1,3,1 ) );
__m256 zxReg = _mm256_shuffle_ps( zReg, xReg, _MM_SHUFFLE( 3,1,2,0 ) );
__m256 reg03 = _mm256_shuffle_ps( xyReg, zxReg, _MM_SHUFFLE( 2, 0, 2, 0 ) );
__m256 reg14 = _mm256_shuffle_ps( yzReg, xyReg, _MM_SHUFFLE( 3, 1, 2, 0 ) );
__m256 reg25 = _mm256_shuffle_ps( zxReg, yzReg, _MM_SHUFFLE( 3, 1, 3, 1 ) );
//Map, xyz
__m128* vertexRegAOS = (__m128*)mTempPtr;
vertexRegAOS[0] = _mm256_castps256_ps128( reg03 ); // x8,y8,z8,x7
vertexRegAOS[1] = _mm256_castps256_ps128( reg14 ); // y7,z7,x6,y6
vertexRegAOS[2] = _mm256_castps256_ps128( reg25 ); // z6,x5,y5,z5
vertexRegAOS[3] = _mm256_extractf128_ps( reg03, 1 ); // x4,y4,z4,x3
vertexRegAOS[4] = _mm256_extractf128_ps( reg14, 1 ); // y3,z3,x2,y2
vertexRegAOS[5] = _mm256_extractf128_ps( reg25, 1 ); // z2,x1,y1,z1
for ( int index = 0, subIndex = 0 ; index < 6; index++ )
{
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
}
memcpy( mappedResource.pData, mVertices, sizeof( ParticleVertex12 ) * NUM_PARTICLES );
deviceContext->Unmap( mVertexBuffer, 0 );
}
应用程序在到达这一行时崩溃
deviceContext->Unmap( mVertexBuffer, 0 );
并显示消息
D3D11 CORRUPTION: ID3D11DeviceContext::Unmap: First parameter is corrupt or NULL. [ MISCELLANEOUS CORRUPTION #13: CORRUPTED_PARAMETER1]
我可能已经找到了问题所在,但由于我对使用AVX相当陌生,我还没有设法解决它
如果我评论这一部分:
//Map, xyz
__m128* vertexRegAOS = (__m128*)mTempPtr;
vertexRegAOS[0] = _mm256_castps256_ps128( reg03 ); // x8,y8,z8,x7
vertexRegAOS[1] = _mm256_castps256_ps128( reg14 ); // y7,z7,x6,y6
vertexRegAOS[2] = _mm256_castps256_ps128( reg25 ); // z6,x5,y5,z5
vertexRegAOS[3] = _mm256_extractf128_ps( reg03, 1 ); // x4,y4,z4,x3
vertexRegAOS[4] = _mm256_extractf128_ps( reg14, 1 ); // y3,z3,x2,y2
vertexRegAOS[5] = _mm256_extractf128_ps( reg25, 1 ); // z2,x1,y1,z1
for ( int index = 0, subIndex = 0 ; index < 6; index++ )
{
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
mVertices[counter++] = vertexRegAOS[index].m128_f32[(subIndex++) % 4];
}
然后它就不会崩溃。类型转换中使用的mTempPtr
的定义类似
mTempPtr = new float[6];
有没有AVX专家可能知道我做错了什么?我很感谢你的建议!
谢谢!
我认为您的错误是为六个32位浮点分配空间,然后存储六个128位浮点向量。你有问题。为下一次分配踩踏记账数据,导致在尝试free()
时出错。
mTempPtr = new float[6];
__m128* vertexRegAOS = (__m128*)mTempPtr;
vertexRegAOS[0] = _mm_setzero_ps();
vertexRegAOS[1] = _mm_setzero_ps(); // buffer overrun here: you only had room for 2 more floats, but you store 4.
vertexRegAOS[2] = ...; // step on more stuff
... // corrupt even more memory :P
您可以通过使用VPERM2F128
,然后使用单个256b存储来保存一两个uop,而不是使用2x VEXTRACTF128
(它显然无法微融合其存储和存储数据uop(。
vertexRegAOS[0] = _mm256_castps256_ps128( reg03 ); // x8,y8,z8,x7
vertexRegAOS[1] = _mm256_castps256_ps128( reg14 ); // y7,z7,x6,y6
vertexRegAOS[2] = _mm256_castps256_ps128( reg25 ); // z6,x5,y5,z5
vertexRegAOS[3] = _mm256_extractf128_ps( reg03, 1 ); // x4,y4,z4,x3
// vertexRegAOS[4] = _mm256_extractf128_ps( reg14, 1 ); // y3,z3,x2,y2
// vertexRegAOS[5] = _mm256_extractf128_ps( reg25, 1 ); // z2,x1,y1,z1
__m256 reg45 = _mm256_permute2f128_ps (reg14, reg25, 1|(3<<4) );
_mm256_storeu_ps( (float*)(vertexRegAOS + 4), reg45);
不过,如果你的代码必须在AMD Piledriver上运行良好,就不要使用256b存储。它有一个糟糕的性能缺陷,使256b存储比两个128b存储慢得多。
另外,从vertexRegAOS
复制到mVertices[counter++]
的循环不就是memcpy
吗?我不明白你为什么不直接存储到它中,如果需要的话,可以使用未对齐的存储。它没有评论,也许我没有花足够的时间盯着它看,如果它没有按顺序复制每个浮动。