Reputation: 11
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
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