Search code examples
c++directxtexturesdirectx-9

ACCESS_VIOLATION exception (0xC0000005) when using IDirect3DTexture9::LockRect


Using DirectX 9, I am trying to create and then fill in an LPDIRECT3DTEXTURE9 texture in the following way.

  1. First, I create the texture with IDirect3DTexture9::CreateTexture:

    LPDIRECT3DTEXTURE9 pTexture;
    
    if ( FAILED( pd3dDevice->CreateTexture( MAX_IMAGE_WIDTH,
                               MAX_IMAGE_HEIGHT,
                               1,
                               0,                 // D3DUSAGE_DYNAMIC,
                               D3DFMT_A8R8G8B8,
                               D3DPOOL_MANAGED,   // D3DPOOL_DEFAULT,
                               &pTexture,
                               NULL ) ) )
    {
        // Handle error case
    }
    
  2. Then, I try and lock a rectangle on the texture as follows:

    unsigned int uiSize = GetTextureSize();
    D3DLOCKED_RECT rect;
    ARGB BlackColor = { (char)0xFF, (char)0xFF, (char)0xFF, (char)0x00 };
    ::ZeroMemory( &rect, sizeof( D3DLOCKED_RECT ) );
    
    // Lock outline texture to rect, and then cast rect to bits and use bits as outlineTexture access point
    
    if ( pTexture == NULL )
    {
        return ERROR_NOT_INITIALIZED;
    }
    
    pTexture->LockRect( 0, &rect, NULL, D3DLOCK_NOSYSLOCK );          // Consider ?
    ARGB* bits = (ARGB*)rect.pBits;
    
    for ( unsigned int uiPixel = 0; uiPixel < uiSize; ++uiPixel )
    {
        // Copy all black pixels only
        if ( compositeMask[uiPixel] == BlackColor )
        {
            bits[uiPixel] = compositeMask[uiPixel];
        }
    }
    
    pTexture->UnlockRect( 0 );
    
    return ERROR_SUCCESS;
    

ARGB is just a struct defined as follows:

struct ARGB
{
    char b;
    char g;
    char r;
    char a;

    bool operator==( ARGB& comp )
    {
        if ( a == comp.a &&
             r == comp.r &&
             g == comp.g &&
             b == comp.b )
            return TRUE;
        else
            return FALSE;
    }

    bool operator!=( ARGB& comp )
    {
        return !( this->operator==( comp ) );
    }
};

What I want to do is pre-calculate an array of pixel data (a black outline) depending on an in-application algorithm, and then only write the pure black pixels from that set of pixel data onto my LPDIRECT3DTEXTURE9 to be rendered later.

The application currently throws a ACCESS_VIOLATION exception (0xC0000005) at the LockRect call. Can anyone possibly explain why?

Here's the exact exception detail:

Unhandled exception at 0x0132F261 in TestApp.exe: 0xC0000005: Access violation reading location 0x00000001.

The location varied between 0x00000000 and 0x00000001... Does that hint at anything?

Also, if there's a better way to do what I am trying to do, then I'd be all ears :)


Solution

  • Like the other commentators on your question, I can't see anything wrong in principle with the way that you create and lock the texture. I have done the same myself - creating a texture in D3DPOOL_MANAGED and using LockRect to update the content.

    However, there are three areas that concern me. I'm posting as an answer because there's far too much for a comment, so please bear with me...

    1. Using the D3DLOCK_NOSYSLOCK flag when locking. I have found that this can cause conflicts when the D3D device has been created for multithreaded operation.
    2. The way you access the locked bits takes no account of the stride. I appreciate that the error apparently occurs before this code, but it's worth mentioning anyway.
    3. You are casting to your own struct for pixel access and it's unclear what the actual size of the struct may be because I can't see your packing options for the project.

    So, I suggest three things that you can do to identify if any of the above are causing a problem:

    First, just use the default zero flag for the locking call

    pTexture->LockRect( 0, &rect, NULL, 0 );
    

    Second, verify that your ARGB structure really is 4 bytes

    ASSERT(sizeof(ARGB) == 4);
    

    Finally, do nothing except lock and unlock the texture and see if you still get a runtime error, but also check the return code

    HRESULT hr = pTexture->LockRect( 0, &rect, NULL, 0 );
    ASSERT(SUCCEEDED(hr));
    hr = pTexture->UnlockRect( 0 );
    ASSERT(SUCCEEDED(hr));
    

    In any case, when updating the texture bits, you must do it on a row-by-row basis, taking account of the stride reported back from the LockRect call in D3DLOCKED_RECT.Pitch.

    Perhaps you could update your question with the results of the above and I can amend this answer as necessary.