bufferdirectx-11.net-8.0silk

Reading one row off texture color data instead of all rows in Silk.Net


I'm testing reading data from texture. The problem is that my final array have incomplete data.

I create my source texture using my own class to simplify the code (https://github.com/Kordi3112/BasicSilkDirect3D11Template/blob/main/Engine/Video/Texture.cs)

 texture = new Texture();

 texture.Create(videoManager.Device, new Vector2i(3, 2), [
     new Color(1, 2, 3, 4), new Color(5, 6, 7, 8), new Color(9, 10, 11, 12), 
     new Color(13, 14, 15, 16), new Color(17, 18, 19, 20), new Color(21, 22, 23, 24)
     ]);

Then I create staging texture:

var testTexture = Texture.CreateStagged(videoManager.Device, texture.Size);

|Texture creation code|:


        public unsafe static ComPtr<ID3D11Texture2D> CreateStagged(ComPtr<ID3D11Device> device, Vector2i size)
        {
            ComPtr<ID3D11Texture2D> texture = default;

            var texture2DDesc = new Texture2DDesc()
            {
                Width = (uint)size.X,
                Height = (uint)size.Y,
                ArraySize = 1,
                BindFlags = (uint)BindFlag.None,
                Usage = Usage.Staging,
                CPUAccessFlags = (uint)CpuAccessFlag.Read,
                Format = Format.FormatR8G8B8A8Unorm,
                MipLevels = 1,
                MiscFlags = (uint)ResourceMiscFlag.None,
                SampleDesc = new SampleDesc(1, 0),
            };

            CrashHandler.CheckForError(device.CreateTexture2D(texture2DDesc, Unsafe.NullRef<SubresourceData>(), ref texture), "Stagged Texture creation error");

            return texture;
        }

Then I copy "texture" to "testTexture", map resource, and copy mapped source data to byte array:

videoManager.DeviceContext.CopyResource(testTexture, texture.texture);

var mappedSubresource = new MappedSubresource();

mappedSubresource.RowPitch = (uint)texture.Size.X * 4; // im not sure if its neccesary

videoManager.DeviceContext.Map(testTexture, 0, Map.Read, 0, ref mappedSubresource);

byte[] data = new byte[texture.Size.X * texture.Size.Y * 4];

fixed (byte* pData = data)
{

    System.Buffer.MemoryCopy(mappedSubresource.PData, pData , data.Length, data.Length);
           
}

videoManager.DeviceContext.Unmap(testTexture, 0);

When i try to see content of data array i have numbers from 0-12 instead of 0-24:

            for (int i = 0; i < data.Length; i++)
            {
                Console.WriteLine((int)data[i]);
            }

image1

I'm sure my source texture is correct, it even shows up on the screen: image2

When i change texture size to [2,3] then it will show data from 0-8, so i think one of 2 things could be wrong:

  1. Mapping resource copy only one dimension to MappedSubresource data
  2. I made mistake on copy data from mappedSubresource.PData to pData

Solution

  • Although silk defines it as ref, mappedSubresource is really an "out" parameter as you can see in the corresponding D3D11 ID3D11DeviceContext::Map method definition

    HRESULT Map(
      [in]            ID3D11Resource           *pResource,
      [in]            UINT                     Subresource,
      [in]            D3D11_MAP                MapType,
      [in]            UINT                     MapFlags,
      [out, optional] D3D11_MAPPED_SUBRESOURCE *pMappedResource
    );
    

    (It's ref in silk to allow you to pass a null pointer since it's also marked as optional)

    When you map a resource, you must use the row pitch defined on the GPU and it may be different than the one you use in the CPU, so to copy it back from GPU, here is how you can do it:

    var mappedSubresource = new MappedSubresource();
    videoManager.DeviceContext.Map(testTexture, 0, Map.Read, 0, ref mappedSubresource);
    
    var dataRowSize = 4 * texture.Size.X;
    byte[] data = new byte[dataRowSize * texture.Size.Y];
    
    // we must copy row by row
    fixed (byte* pData = data)
    {
        for (var i = 0; i < texture.Size.Y; i++)
        {
            System.Buffer.MemoryCopy((byte*)mappedSubresource.PData + i * mappedSubresource.RowPitch, pData + i * dataRowSize, dataRowSize, dataRowSize);
        }
    }
    
    videoManager.DeviceContext.Unmap(testTexture, 0);