Zermil
Zermil

Reputation: 11

D3D11 How to properly copy from swap_chain/render target

I've been trying to write a function that simply takes the backbuffer/framebuffer and saves its contents to a file.

internal unsigned char *r_get_backbuffer(Arena *arena, GFX_Window *window, int width, int height)
{
  D3D11_Window *w = d3d11_window_from_opaque(window);
            
  int bytes = width*height*4;
  result = arena_push_array(arena, u8, bytes); // @Note: This just allocates memory

  ID3D11Texture2D *surface = 0;
  w->swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **) &surface);

  D3D11_TEXTURE2D_DESC desc = {0};
  desc.ArraySize = 1;
  desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  desc.Width = width;
  desc.Height = height;
  desc.MipLevels = 1;
  desc.SampleDesc.Count = 1;
  desc.SampleDesc.Quality = 0;
  desc.BindFlags = 0;
  desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  desc.Usage = D3D11_USAGE_STAGING;

  ID3D11Texture2D *tex = 0;
  d3d11_state.device->CreateTexture2D(&desc, 0, &tex);
  d3d11_state.context->CopyResource(tex, surface);
                
  D3D11_MAPPED_SUBRESOURCE texture_resource = {0};
  d3d11_state.context->Map(tex, 0, D3D11_MAP_READ, 0, &texture_resource);
  MemoryCopy(result, texture_resource.pData, bytes); // @Note: Just a wrapper for memcpy()
  d3d11_state.context->Unmap(tex, 0);
                    
  tex->Release();
  surface->Release();
}

The above function works well for sizes that are specifically 16:9: 1280x720 image

However there's a problem when size is anything else: 1000x1000 image

This makes me think that CopyResource(); call (or similar) adds some of its own padding? I'm using stb image write for the image writing and my call looks like so:

int w = 1000;
int h = 1000;
u8 *pixels = r_get_backbuffer(frame_arena, window, w, h);
stbi_write_jpg("ss.jpg", w, h, 4, pixels, w*4);
// ...

Is there something I'm missing? I thought that it might be an issue with 'stride' but no matter what I try it won't budge. I might have missed something in the docs, I've been trying to find an answer for a couple days now and google isn't really helpful nowadays because of its own bloat.

An ideal solution would copy the contents of swap chain/render target view into an array of bytes (color values) properly no matter the size. I don't really care about the specific implementation, I just want to be able to save framebuffer to a file or get its contents as an array of pixels.

Upvotes: 0

Views: 47

Answers (1)

Zermil
Zermil

Reputation: 11

Terribly sorry for any confusion, I'm going to leave the answer here in case someone else has a similar problem (assuming google is being helpful). My issue was indeed with the stride, I was copying from the texture (whose stride was RowPitch) to my buffer of pixels (whose stride was width*4). Here's the working code just for copying from the texture:

internal unsigned char *r_get_backbuffer(Arena *arena, GFX_Window *window, int width, int height)
{
  D3D11_Window *w = d3d11_window_from_opaque(window);
            
  int bytes = width*height*4;
  result = arena_push_array(arena, u8, bytes); // @Note: This just allocates memory

  ID3D11Texture2D *surface = 0;
  w->swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **) &surface);

  D3D11_TEXTURE2D_DESC desc = {0};
  surface->GetDesc(&desc);
  desc.BindFlags = 0;
  desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
  desc.Usage = D3D11_USAGE_STAGING;

  ID3D11Texture2D *tex = 0;
  d3d11_state.device->CreateTexture2D(&desc, 0, &tex);
  d3d11_state.context->CopyResource(tex, surface);
                
  D3D11_MAPPED_SUBRESOURCE texture_resource = {0};
  d3d11_state.context->Map(tex, 0, D3D11_MAP_READ, 0, &texture_resource);

  // @Note: This part was incorrect previously, now corrected
  u8 *dst = result;
  u8 *src = ((u8 *) texture_resource.pData);
  for (s32 i = 0; i < height; ++i) {
    MemoryCopy(dst, src, width*4);
    dst += width*4;
    src += texture_resource.RowPitch;
  }
  // ==============

  d3d11_state.context->Unmap(tex, 0);
                    
  tex->Release();
  surface->Release();
}

Upvotes: 1

Related Questions