DirectX 11-使用AVX的AoS到SoA转换导致重映射时顶点缓冲区损坏




我正在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_DYNAMICD3D11_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吗?我不明白你为什么不直接存储到它中,如果需要的话,可以使用未对齐的存储。它没有评论,也许我没有花足够的时间盯着它看,如果它没有按顺序复制每个浮动。

最新更新