From 264b296b7dc9fe18dd423323969a3e3820794e4c Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Fri, 31 Aug 2018 15:27:22 -0700 Subject: [PATCH] Save .rdc file extthumbnail as PNG In order to embed the original rendered image during the capture in its original form, without incurring JPEG pixel artifacts, use PNG file format for thumbnails. Change-Id: I14ff487856949cd6f83889507e16feb4c50a40fb --- renderdoc/core/core.cpp | 164 +++++++++++++++++++--- renderdoc/core/core.h | 26 +++- renderdoc/driver/d3d11/d3d11_device.cpp | 139 +++++-------------- renderdoc/driver/d3d12/d3d12_device.cpp | 135 ++++--------------- renderdoc/driver/gl/gl_driver.cpp | 128 +++--------------- renderdoc/driver/gl/gl_driver.h | 14 +- renderdoc/driver/vulkan/vk_core.cpp | 172 +++++------------------- 7 files changed, 283 insertions(+), 495 deletions(-) diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index 269d1911e..27737a222 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -29,9 +29,11 @@ #include "api/replay/version.h" #include "common/common.h" #include "hooks/hooks.h" +#include "maths/formatpacking.h" #include "replay/replay_driver.h" #include "serialise/rdcfile.h" #include "serialise/serialiser.h" +#include "stb/stb_image_write.h" #include "strings/string_utils.h" #include "crash_handler.h" @@ -716,8 +718,142 @@ bool RenderDoc::ShouldTriggerCapture(uint32_t frameNumber) return ret; } -RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixels, size_t thlen, - uint16_t thwidth, uint16_t thheight, FileType thformat) +void RenderDoc::ResamplePixels(const FramePixels &in, RDCThumb &out) +{ + // code below assumes pitch_requirement is a power of 2 number + RDCASSERT((in.pitch_requirement & (in.pitch_requirement - 1)) == 0); + + out.width = (uint16_t)RDCMIN(in.max_width, in.width); + out.width &= ~(in.pitch_requirement - 1); // align down to multiple of in. + out.height = uint16_t(out.width * in.height / in.width); + out.len = 3 * out.width * out.height; + out.pixels = new byte[out.len]; + out.format = FileType::Raw; + + byte *dst = (byte *)out.pixels; + byte *source = (byte *)in.data; + + for(uint32_t y = 0; y < out.height; y++) + { + for(uint32_t x = 0; x < out.width; x++) + { + uint32_t xSource = x * in.width / out.width; + uint32_t ySource = y * in.height / out.height; + byte *src = &source[in.stride * xSource + in.pitch * ySource]; + + if(in.buf1010102) + { + uint32_t *src1010102 = (uint32_t *)src; + Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); + dst[0] = (byte)(unorm.x * 255.0f); + dst[1] = (byte)(unorm.y * 255.0f); + dst[2] = (byte)(unorm.z * 255.0f); + } + else if(in.buf565) + { + uint16_t *src565 = (uint16_t *)src; + Vec3f unorm = ConvertFromB5G6R5(*src565); + dst[0] = (byte)(unorm.z * 255.0f); + dst[1] = (byte)(unorm.y * 255.0f); + dst[2] = (byte)(unorm.x * 255.0f); + } + else if(in.buf5551) + { + uint16_t *src5551 = (uint16_t *)src; + Vec4f unorm = ConvertFromB5G5R5A1(*src5551); + dst[0] = (byte)(unorm.z * 255.0f); + dst[1] = (byte)(unorm.y * 255.0f); + dst[2] = (byte)(unorm.x * 255.0f); + } + else if(in.bgra) + { + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + } + else if(in.bpc == 2) // R16G16B16A16 backbuffer + { + uint16_t *src16 = (uint16_t *)src; + + float linearR = RDCCLAMP(ConvertFromHalf(src16[0]), 0.0f, 1.0f); + float linearG = RDCCLAMP(ConvertFromHalf(src16[1]), 0.0f, 1.0f); + float linearB = RDCCLAMP(ConvertFromHalf(src16[2]), 0.0f, 1.0f); + + if(linearR < 0.0031308f) + dst[0] = byte(255.0f * (12.92f * linearR)); + else + dst[0] = byte(255.0f * (1.055f * powf(linearR, 1.0f / 2.4f) - 0.055f)); + + if(linearG < 0.0031308f) + dst[1] = byte(255.0f * (12.92f * linearG)); + else + dst[1] = byte(255.0f * (1.055f * powf(linearG, 1.0f / 2.4f) - 0.055f)); + + if(linearB < 0.0031308f) + dst[2] = byte(255.0f * (12.92f * linearB)); + else + dst[2] = byte(255.0f * (1.055f * powf(linearB, 1.0f / 2.4f) - 0.055f)); + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + dst += 3; + } + } + + if(!in.is_y_flipped) + { + for(uint16_t y = 0; y <= out.height / 2; y++) + { + uint16_t flipY = (out.height - 1 - y); + for(uint16_t x = 0; x < out.width; x++) + { + byte *src = (byte *)out.pixels; + byte save[3]; + save[0] = src[(y * out.width + x) * 3 + 0]; + save[1] = src[(y * out.width + x) * 3 + 1]; + save[2] = src[(y * out.width + x) * 3 + 2]; + + src[(y * out.width + x) * 3 + 0] = src[(flipY * out.width + x) * 3 + 0]; + src[(y * out.width + x) * 3 + 1] = src[(flipY * out.width + x) * 3 + 1]; + src[(y * out.width + x) * 3 + 2] = src[(flipY * out.width + x) * 3 + 2]; + + src[(flipY * out.width + x) * 3 + 0] = save[0]; + src[(flipY * out.width + x) * 3 + 1] = save[1]; + src[(flipY * out.width + x) * 3 + 2] = save[2]; + } + } + } +} +void RenderDoc::EncodePixelsPNG(const RDCThumb &in, RDCThumb &out) +{ + struct WriteCallbackData + { + std::vector buffer; + + static void writeData(void *context, void *data, int size) + { + WriteCallbackData *pThis = (WriteCallbackData *)context; + const byte *start = (const byte *)data; + pThis->buffer.insert(pThis->buffer.end(), start, start + size); + } + }; + + WriteCallbackData callbackData; + stbi_write_png_to_func(&WriteCallbackData::writeData, &callbackData, in.width, in.height, 3, + in.pixels, 0); + out.width = in.width; + out.height = in.height; + out.pixels = new byte[callbackData.buffer.size()]; + memcpy((void *)out.pixels, callbackData.buffer.data(), callbackData.buffer.size()); + out.len = (uint32_t)callbackData.buffer.size(); + out.format = FileType::PNG; +} + +RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePixels &fp) { RDCFile *ret = new RDCFile; @@ -737,22 +873,17 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixel } } - RDCThumb th; - RDCThumb *thumb = NULL; - - if(thpixels) + RDCThumb outRaw, outPng; + if(fp.data) { - th.len = (uint32_t)thlen; - th.pixels = (const byte *)thpixels; - th.width = thwidth; - th.height = thheight; - RDCASSERT(thformat == FileType::JPG || thformat == FileType::PNG || thformat == FileType::BMP || - thformat == FileType::TGA || thformat == FileType::Raw); - th.format = thformat; - thumb = &th; + // point sample info into raw buffer + ResamplePixels(fp, outRaw); + EncodePixelsPNG(outRaw, outPng); } - ret->SetData(driver, ToStr(driver).c_str(), OSUtility::GetMachineIdent(), thumb); + RDCASSERT(outPng.pixels != NULL); + + ret->SetData(driver, ToStr(driver).c_str(), OSUtility::GetMachineIdent(), &outPng); FileIO::CreateParentDirectory(m_CurrentLogFile); @@ -764,6 +895,9 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixel SAFE_DELETE(ret); } + SAFE_DELETE_ARRAY(outRaw.pixels); + SAFE_DELETE_ARRAY(outPng.pixels); + return ret; } diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h index 3b4e9ebf2..9c4af691c 100644 --- a/renderdoc/core/core.h +++ b/renderdoc/core/core.h @@ -45,6 +45,7 @@ using std::pair; using std::set; class Chunk; +struct RDCThumb; // not provided by tinyexr, just do by hand bool is_exr_file(FILE *f); @@ -346,6 +347,26 @@ typedef void (*ShutdownFunction)(); class RenderDoc { public: + struct FramePixels + { + uint8_t *data = NULL; + uint32_t len = 0; + uint32_t width = 0; + uint32_t pitch = 0; + uint32_t height = 0; + uint32_t stride = 0; + uint32_t bpc = 0; // bytes per channel + bool buf1010102 = false; + bool buf565 = false; + bool buf5551 = false; + bool bgra = false; + bool is_y_flipped = true; + uint32_t pitch_requirement = 0; + uint32_t max_width = 0; + FramePixels() {} + ~FramePixels() { SAFE_DELETE_ARRAY(data); } + }; + static RenderDoc &Inst(); template @@ -410,8 +431,9 @@ public: void RecreateCrashHandler(); void UnloadCrashHandler(); ICrashHandler *GetCrashHandler() const { return m_ExHandler; } - RDCFile *CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixels, size_t thlen, - uint16_t thwidth, uint16_t thheight, FileType thformat); + void ResamplePixels(const FramePixels &in, RDCThumb &out); + void EncodePixelsPNG(const RDCThumb &in, RDCThumb &out); + RDCFile *CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePixels &fp); void FinishCaptureWriting(RDCFile *rdc, uint32_t frameNumber); void AddChildProcess(uint32_t pid, uint32_t ident) diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 163e672e1..9b104d9c7 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -1543,10 +1543,7 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) } const uint32_t maxSize = 2048; - - byte *thpixels = NULL; - uint16_t thwidth = 0; - uint16_t thheight = 0; + RenderDoc::FramePixels fp; if(swap != NULL) { @@ -1626,85 +1623,36 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) } else { - byte *data = (byte *)mapped.pData; - - thwidth = (uint16_t)RDCMIN(maxSize, desc.Width); - thwidth &= ~0x7; // align down to multiple of 8 - thheight = uint16_t(thwidth * desc.Height / desc.Width); - - thpixels = new byte[3U * thwidth * thheight]; - - uint32_t stride = fmt.compByteWidth * fmt.compCount; - - bool buf1010102 = false; - bool bufBGRA = (fmt.bgraOrder != false); - - if(fmt.type == ResourceFormatType::R10G10B10A2) - { - stride = 4; - buf1010102 = true; - } - - byte *dst = thpixels; - - for(uint32_t y = 0; y < thheight; y++) - { - for(uint32_t x = 0; x < thwidth; x++) - { - uint32_t xSource = x * desc.Width / thwidth; - uint32_t ySource = y * desc.Height / thheight; - - byte *src = &data[stride * xSource + mapped.RowPitch * ySource]; - - if(buf1010102) - { - uint32_t *src1010102 = (uint32_t *)src; - Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); - dst[0] = (byte)(unorm.x * 255.0f); - dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.z * 255.0f); - } - else if(bufBGRA) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - } - else if(fmt.compByteWidth == 2) // R16G16B16A16 backbuffer - { - uint16_t *src16 = (uint16_t *)src; - - float linearR = RDCCLAMP(ConvertFromHalf(src16[0]), 0.0f, 1.0f); - float linearG = RDCCLAMP(ConvertFromHalf(src16[1]), 0.0f, 1.0f); - float linearB = RDCCLAMP(ConvertFromHalf(src16[2]), 0.0f, 1.0f); - - if(linearR < 0.0031308f) - dst[0] = byte(255.0f * (12.92f * linearR)); - else - dst[0] = byte(255.0f * (1.055f * powf(linearR, 1.0f / 2.4f) - 0.055f)); - - if(linearG < 0.0031308f) - dst[1] = byte(255.0f * (12.92f * linearG)); - else - dst[1] = byte(255.0f * (1.055f * powf(linearG, 1.0f / 2.4f) - 0.055f)); - - if(linearB < 0.0031308f) - dst[2] = byte(255.0f * (12.92f * linearB)); - else - dst[2] = byte(255.0f * (1.055f * powf(linearB, 1.0f / 2.4f) - 0.055f)); - } - else - { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - } - - dst += 3; - } - } + fp.len = (uint32_t)mapped.RowPitch * desc.Height; + fp.data = new uint8_t[fp.len]; + memcpy(fp.data, mapped.pData, fp.len); m_pImmediateContext->GetReal()->Unmap(stagingTex, 0); + + fp.width = (uint32_t)desc.Width; + fp.height = (uint32_t)desc.Height; + fp.pitch = mapped.RowPitch; + fp.stride = fmt.compByteWidth * fmt.compCount; + fp.bpc = fmt.compByteWidth; + fp.bgra = fmt.bgraOrder; + fp.max_width = maxSize; + fp.pitch_requirement = 8; + switch(fmt.type) + { + case ResourceFormatType::R10G10B10A2: + fp.stride = 4; + fp.buf1010102 = true; + break; + case ResourceFormatType::R5G6B5: + fp.stride = 2; + fp.buf565 = true; + break; + case ResourceFormatType::R5G5B5A1: + fp.stride = 2; + fp.buf5551 = true; + break; + default: break; + } } } @@ -1712,33 +1660,8 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) } } - byte *jpgbuf = NULL; - int len = thwidth * thheight; - - if(wnd) - { - jpgbuf = new byte[len]; - - jpge::params p; - p.m_quality = 80; - - bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, - thpixels, p); - - if(!success) - { - RDCERR("Failed to compress to jpg"); - SAFE_DELETE_ARRAY(jpgbuf); - thwidth = 0; - thheight = 0; - } - } - - RDCFile *rdc = RenderDoc::Inst().CreateRDC(RDCDriver::D3D11, m_CapturedFrames.back().frameNumber, - jpgbuf, len, thwidth, thheight, FileType::JPG); - - SAFE_DELETE_ARRAY(jpgbuf); - SAFE_DELETE_ARRAY(thpixels); + RDCFile *rdc = + RenderDoc::Inst().CreateRDC(RDCDriver::D3D11, m_CapturedFrames.back().frameNumber, fp); StreamWriter *captureWriter = NULL; diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 0c2d67c0e..922196022 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -1530,11 +1530,8 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) GetWrapped(it->res)->FreeShadow(); } - byte *thpixels = NULL; - uint16_t thwidth = 0; - uint16_t thheight = 0; - const uint32_t maxSize = 2048; + RenderDoc::FramePixels fp; // gather backbuffer screenshot if(backbuffer != NULL) @@ -1547,7 +1544,6 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) heapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC bufDesc; - bufDesc.Alignment = 0; bufDesc.DepthOrArraySize = 1; bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; @@ -1614,84 +1610,35 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) if(SUCCEEDED(hr) && data) { + fp.len = (uint32_t)bufDesc.Width; + fp.data = new uint8_t[fp.len]; + memcpy(fp.data, data, fp.len); + ResourceFormat fmt = MakeResourceFormat(desc.Format); - - thwidth = (uint16_t)RDCMIN(maxSize, (uint32_t)desc.Width); - thwidth &= ~0x7; // align down to multiple of 8 - thheight = uint16_t(thwidth * desc.Height / desc.Width); - - thpixels = new byte[3U * thwidth * thheight]; - - uint32_t stride = fmt.compByteWidth * fmt.compCount; - - bool buf1010102 = false; - bool bufBGRA = (fmt.bgraOrder != false); - - if(fmt.type == ResourceFormatType::R10G10B10A2) + fp.width = (uint32_t)desc.Width; + fp.height = (uint32_t)desc.Height; + fp.pitch = layout.Footprint.RowPitch; + fp.stride = fmt.compByteWidth * fmt.compCount; + fp.bpc = fmt.compByteWidth; + fp.bgra = fmt.bgraOrder; + fp.max_width = maxSize; + fp.pitch_requirement = 8; + switch(fmt.type) { - stride = 4; - buf1010102 = true; + case ResourceFormatType::R10G10B10A2: + fp.stride = 4; + fp.buf1010102 = true; + break; + case ResourceFormatType::R5G6B5: + fp.stride = 2; + fp.buf565 = true; + break; + case ResourceFormatType::R5G5B5A1: + fp.stride = 2; + fp.buf5551 = true; + break; + default: break; } - - byte *dstPixels = thpixels; - - for(uint32_t y = 0; y < thheight; y++) - { - for(uint32_t x = 0; x < thwidth; x++) - { - uint32_t xSource = uint32_t(x * desc.Width / thwidth); - uint32_t ySource = uint32_t(y * desc.Height / thheight); - - byte *srcPixels = &data[stride * xSource + layout.Footprint.RowPitch * ySource]; - - if(buf1010102) - { - uint32_t *src1010102 = (uint32_t *)srcPixels; - Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); - dstPixels[0] = (byte)(unorm.x * 255.0f); - dstPixels[1] = (byte)(unorm.y * 255.0f); - dstPixels[2] = (byte)(unorm.z * 255.0f); - } - else if(bufBGRA) - { - dstPixels[0] = srcPixels[2]; - dstPixels[1] = srcPixels[1]; - dstPixels[2] = srcPixels[0]; - } - else if(fmt.compByteWidth == 2) // R16G16B16A16 backbuffer - { - uint16_t *src16 = (uint16_t *)srcPixels; - - float linearR = RDCCLAMP(ConvertFromHalf(src16[0]), 0.0f, 1.0f); - float linearG = RDCCLAMP(ConvertFromHalf(src16[1]), 0.0f, 1.0f); - float linearB = RDCCLAMP(ConvertFromHalf(src16[2]), 0.0f, 1.0f); - - if(linearR < 0.0031308f) - dstPixels[0] = byte(255.0f * (12.92f * linearR)); - else - dstPixels[0] = byte(255.0f * (1.055f * powf(linearR, 1.0f / 2.4f) - 0.055f)); - - if(linearG < 0.0031308f) - dstPixels[1] = byte(255.0f * (12.92f * linearG)); - else - dstPixels[1] = byte(255.0f * (1.055f * powf(linearG, 1.0f / 2.4f) - 0.055f)); - - if(linearB < 0.0031308f) - dstPixels[2] = byte(255.0f * (12.92f * linearB)); - else - dstPixels[2] = byte(255.0f * (1.055f * powf(linearB, 1.0f / 2.4f) - 0.055f)); - } - else - { - dstPixels[0] = srcPixels[0]; - dstPixels[1] = srcPixels[1]; - dstPixels[2] = srcPixels[2]; - } - - dstPixels += 3; - } - } - copyDst->Unmap(0, NULL); } else @@ -1707,39 +1654,13 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) } } - byte *jpgbuf = NULL; - int len = thwidth * thheight; - - if(wnd && thpixels) - { - jpgbuf = new byte[len]; - - jpge::params p; - p.m_quality = 80; - - bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, - thpixels, p); - - if(!success) - { - RDCERR("Failed to compress to jpg"); - SAFE_DELETE_ARRAY(jpgbuf); - thwidth = 0; - thheight = 0; - } - } - queues = m_Queues; for(auto it = queues.begin(); it != queues.end(); ++it) if((*it)->GetResourceRecord()->ContainsExecuteIndirect) WrappedID3D12Resource::RefBuffers(GetResourceManager()); - rdc = RenderDoc::Inst().CreateRDC(RDCDriver::D3D12, m_CapturedFrames.back().frameNumber, jpgbuf, - len, thwidth, thheight, FileType::JPG); - - SAFE_DELETE_ARRAY(jpgbuf); - SAFE_DELETE_ARRAY(thpixels); + rdc = RenderDoc::Inst().CreateRDC(RDCDriver::D3D12, m_CapturedFrames.back().frameNumber, fp); } StreamWriter *captureWriter = NULL; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index b024061b0..a73d4506e 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -1875,7 +1875,7 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) ContextEndFrame(); FinishCapture(); - BackbufferImage *bbim = NULL; + RenderDoc::FramePixels *bbim = NULL; // if the specified context isn't current, try and see if we've saved // an appropriate backbuffer image during capture. @@ -1895,9 +1895,8 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) if(bbim == NULL) bbim = SaveBackbufferImage(); - RDCFile *rdc = RenderDoc::Inst().CreateRDC(GetDriverType(), m_CapturedFrames.back().frameNumber, - bbim->jpgbuf, bbim->len, bbim->thwidth, - bbim->thheight, FileType::JPG); + RDCFile *rdc = + RenderDoc::Inst().CreateRDC(GetDriverType(), m_CapturedFrames.back().frameNumber, bbim[0]); SAFE_DELETE(bbim); @@ -2106,13 +2105,10 @@ void WrappedOpenGL::FirstFrame(void *ctx, void *wndHandle) } } -WrappedOpenGL::BackbufferImage *WrappedOpenGL::SaveBackbufferImage() +RenderDoc::FramePixels *WrappedOpenGL::SaveBackbufferImage() { const uint16_t maxSize = 2048; - - byte *thpixels = NULL; - uint16_t thwidth = 0; - uint16_t thheight = 0; + RenderDoc::FramePixels *fp = new RenderDoc::FramePixels(); if(GL.glGetIntegerv && GL.glReadBuffer && GL.glBindFramebuffer && GL.glBindBuffer && GL.glReadPixels) { @@ -2141,49 +2137,19 @@ WrappedOpenGL::BackbufferImage *WrappedOpenGL::SaveBackbufferImage() ContextData &dat = GetCtxData(); - thwidth = (uint16_t)dat.initParams.width; - thheight = (uint16_t)dat.initParams.height; - - thpixels = new byte[thwidth * thheight * 4]; + fp->width = dat.initParams.width; + fp->height = dat.initParams.height; + fp->bpc = 1; + fp->stride = fp->bpc * 4; + fp->pitch = dat.initParams.width * fp->stride; + fp->max_width = maxSize; + fp->pitch_requirement = 4; + fp->len = (uint32_t)fp->pitch * fp->height; + fp->data = new uint8_t[fp->len]; + fp->is_y_flipped = dat.initParams.isYFlipped; // GLES only supports GL_RGBA - GL.glReadPixels(0, 0, thwidth, thheight, eGL_RGBA, eGL_UNSIGNED_BYTE, thpixels); - - // RGBA -> RGB - for(uint32_t y = 0; y < thheight; y++) - { - for(uint32_t x = 0; x < thwidth; x++) - { - thpixels[(y * thwidth + x) * 3 + 0] = thpixels[(y * thwidth + x) * 4 + 0]; - thpixels[(y * thwidth + x) * 3 + 1] = thpixels[(y * thwidth + x) * 4 + 1]; - thpixels[(y * thwidth + x) * 3 + 2] = thpixels[(y * thwidth + x) * 4 + 2]; - } - } - - // flip the image in-place - if(!dat.initParams.isYFlipped) - { - for(uint16_t y = 0; y <= thheight / 2; y++) - { - uint16_t flipY = (thheight - 1 - y); - - for(uint16_t x = 0; x < thwidth; x++) - { - byte save[3]; - save[0] = thpixels[(y * thwidth + x) * 3 + 0]; - save[1] = thpixels[(y * thwidth + x) * 3 + 1]; - save[2] = thpixels[(y * thwidth + x) * 3 + 2]; - - thpixels[(y * thwidth + x) * 3 + 0] = thpixels[(flipY * thwidth + x) * 3 + 0]; - thpixels[(y * thwidth + x) * 3 + 1] = thpixels[(flipY * thwidth + x) * 3 + 1]; - thpixels[(y * thwidth + x) * 3 + 2] = thpixels[(flipY * thwidth + x) * 3 + 2]; - - thpixels[(flipY * thwidth + x) * 3 + 0] = save[0]; - thpixels[(flipY * thwidth + x) * 3 + 1] = save[1]; - thpixels[(flipY * thwidth + x) * 3 + 2] = save[2]; - } - } - } + GL.glReadPixels(0, 0, fp->width, fp->height, eGL_RGBA, eGL_UNSIGNED_BYTE, fp->data); GL.glBindBuffer(eGL_PIXEL_PACK_BUFFER, packBufBind); GL.glBindFramebuffer(eGL_READ_FRAMEBUFFER, prevBuf); @@ -2192,69 +2158,9 @@ WrappedOpenGL::BackbufferImage *WrappedOpenGL::SaveBackbufferImage() GL.glPixelStorei(eGL_PACK_SKIP_ROWS, prevPackSkipRows); GL.glPixelStorei(eGL_PACK_SKIP_PIXELS, prevPackSkipPixels); GL.glPixelStorei(eGL_PACK_ALIGNMENT, prevPackAlignment); - - // scale down if necessary using simple point sampling - thwidth = RDCMIN(maxSize, uint16_t(dat.initParams.width)); - // TODO(akharlamov): why does OpenGL align to 4 when Vulkan and DX11 align to 8? - thwidth &= ~0x3; // align down to multiple of 4 - thheight = uint16_t(thwidth * dat.initParams.height / dat.initParams.width); - - byte *src = thpixels; - byte *dst = thpixels = new byte[3U * thwidth * thheight]; - - for(uint32_t y = 0; y < thheight; y++) - { - for(uint32_t x = 0; x < thwidth; x++) - { - uint32_t xSource = x * dat.initParams.width / thwidth; - uint32_t ySource = y * dat.initParams.height / thheight; - - byte *pixelsrc = &src[3 * xSource + dat.initParams.width * 3 * ySource]; - - memcpy(dst, pixelsrc, 3); - - dst += 3; - } - } - - // src is the raw unscaled pixels, which is no longer needed - SAFE_DELETE_ARRAY(src); } - byte *jpgbuf = NULL; - int len = thwidth * thheight; - - if(len > 0) - { - // jpge::compress_image_to_jpeg_file_in_memory requires at least 1024 bytes - len = len >= 1024 ? len : 1024; - - jpgbuf = new byte[len]; - - jpge::params p; - p.m_quality = 80; - - bool success = - jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p); - - if(!success) - { - RDCERR("Failed to compress to jpg"); - SAFE_DELETE_ARRAY(jpgbuf); - thwidth = 0; - thheight = 0; - } - } - - SAFE_DELETE_ARRAY(thpixels); - - BackbufferImage *bbim = new BackbufferImage(); - bbim->jpgbuf = jpgbuf; - bbim->len = len; - bbim->thwidth = thwidth; - bbim->thheight = thheight; - - return bbim; + return fp; } template diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 07d6ec3ee..cf9c524b5 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -459,21 +459,11 @@ private: void RenderOverlayText(float x, float y, const char *fmt, ...); void RenderOverlayStr(float x, float y, const char *str); - struct BackbufferImage - { - BackbufferImage() : jpgbuf(NULL), len(0), thwidth(0), thheight(0) {} - ~BackbufferImage() { SAFE_DELETE_ARRAY(jpgbuf); } - byte *jpgbuf; - size_t len; - uint16_t thwidth; - uint16_t thheight; - }; - void CreateReplayBackbuffer(const GLInitParams ¶ms, ResourceId fboOrigId, GLuint &fbo, std::string bbname); - BackbufferImage *SaveBackbufferImage(); - map m_BackbufferImages; + RenderDoc::FramePixels *SaveBackbufferImage(); + map m_BackbufferImages; void BuildGLExtensions(); void BuildGLESExtensions(); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 1f6c540bf..c3d21c3cb 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -30,6 +30,8 @@ #include "strings/string_utils.h" #include "vk_debug.h" +#include "stb/stb_image_write.h" + uint32_t VkInitParams::GetSerialiseSize() { // misc bytes and fixed integer members @@ -1366,12 +1368,9 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) } } - byte *thpixels = NULL; - uint16_t thwidth = 0; - uint16_t thheight = 0; - // gather backbuffer screenshot const uint32_t maxSize = 2048; + RenderDoc::FramePixels fp; if(swap != VK_NULL_HANDLE) { @@ -1512,9 +1511,12 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) vkr = vt->MapMemory(Unwrap(device), Unwrap(readbackMem.mem), readbackMem.offs, readbackMem.size, 0, (void **)&pData); RDCASSERTEQUAL(vkr, VK_SUCCESS); - RDCASSERT(pData != NULL); + fp.len = (uint32_t)readbackMem.size; + fp.data = new uint8_t[fp.len]; + memcpy(fp.data, pData, fp.len); + VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, @@ -1526,151 +1528,41 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) vkr = vt->InvalidateMappedMemoryRanges(Unwrap(device), 1, &range); RDCASSERTEQUAL(vkr, VK_SUCCESS); - // point sample info into raw buffer - { - ResourceFormat fmt = MakeResourceFormat(swapInfo.format); - - byte *data = (byte *)pData; - - thwidth = (uint16_t)RDCMIN(maxSize, swapInfo.extent.width); - thwidth &= ~0x7; // align down to multiple of 8 - thheight = uint16_t(thwidth * swapInfo.extent.height / swapInfo.extent.width); - - thpixels = new byte[3U * thwidth * thheight]; - - uint32_t stride = fmt.compByteWidth * fmt.compCount; - - bool buf1010102 = false; - bool buf565 = false, buf5551 = false; - bool bufBGRA = (fmt.bgraOrder != false); - - switch(fmt.type) - { - case ResourceFormatType::R10G10B10A2: - stride = 4; - buf1010102 = true; - break; - case ResourceFormatType::R5G6B5: - stride = 2; - buf565 = true; - break; - case ResourceFormatType::R5G5B5A1: - stride = 2; - buf5551 = true; - break; - default: break; - } - - byte *dst = thpixels; - - for(uint32_t y = 0; y < thheight; y++) - { - for(uint32_t x = 0; x < thwidth; x++) - { - uint32_t xSource = x * swapInfo.extent.width / thwidth; - uint32_t ySource = y * swapInfo.extent.height / thheight; - - byte *src = &data[stride * xSource + rowPitch * ySource]; - - if(buf1010102) - { - uint32_t *src1010102 = (uint32_t *)src; - Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); - dst[0] = (byte)(unorm.x * 255.0f); - dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.z * 255.0f); - } - else if(buf565) - { - uint16_t *src565 = (uint16_t *)src; - Vec3f unorm = ConvertFromB5G6R5(*src565); - dst[0] = (byte)(unorm.z * 255.0f); - dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.x * 255.0f); - } - else if(buf5551) - { - uint16_t *src5551 = (uint16_t *)src; - Vec4f unorm = ConvertFromB5G5R5A1(*src5551); - dst[0] = (byte)(unorm.z * 255.0f); - dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.x * 255.0f); - } - else if(bufBGRA) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - } - else if(fmt.compByteWidth == 2) // R16G16B16A16 backbuffer - { - uint16_t *src16 = (uint16_t *)src; - - float linearR = RDCCLAMP(ConvertFromHalf(src16[0]), 0.0f, 1.0f); - float linearG = RDCCLAMP(ConvertFromHalf(src16[1]), 0.0f, 1.0f); - float linearB = RDCCLAMP(ConvertFromHalf(src16[2]), 0.0f, 1.0f); - - if(linearR < 0.0031308f) - dst[0] = byte(255.0f * (12.92f * linearR)); - else - dst[0] = byte(255.0f * (1.055f * powf(linearR, 1.0f / 2.4f) - 0.055f)); - - if(linearG < 0.0031308f) - dst[1] = byte(255.0f * (12.92f * linearG)); - else - dst[1] = byte(255.0f * (1.055f * powf(linearG, 1.0f / 2.4f) - 0.055f)); - - if(linearB < 0.0031308f) - dst[2] = byte(255.0f * (12.92f * linearB)); - else - dst[2] = byte(255.0f * (1.055f * powf(linearB, 1.0f / 2.4f) - 0.055f)); - } - else - { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - } - - dst += 3; - } - } - } - vt->UnmapMemory(Unwrap(device), Unwrap(readbackMem.mem)); // delete all vt->DestroyBuffer(Unwrap(device), Unwrap(readbackBuf), NULL); GetResourceManager()->ReleaseWrappedResource(readbackBuf); - } - byte *jpgbuf = NULL; - int len = thwidth * thheight; - - if(wnd) - { - jpgbuf = new byte[len]; - - jpge::params p; - p.m_quality = 80; - - bool success = - jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p); - - if(!success) + ResourceFormat fmt = MakeResourceFormat(swapInfo.format); + fp.width = swapInfo.extent.width; + fp.height = swapInfo.extent.height; + fp.pitch = rowPitch; + fp.stride = fmt.compByteWidth * fmt.compCount; + fp.bpc = fmt.compByteWidth; + fp.bgra = fmt.bgraOrder; + fp.max_width = maxSize; + fp.pitch_requirement = 8; + switch(fmt.type) { - RDCERR("Failed to compress to jpg"); - SAFE_DELETE_ARRAY(jpgbuf); - thwidth = 0; - thheight = 0; + case ResourceFormatType::R10G10B10A2: + fp.stride = 4; + fp.buf1010102 = true; + break; + case ResourceFormatType::R5G6B5: + fp.stride = 2; + fp.buf565 = true; + break; + case ResourceFormatType::R5G5B5A1: + fp.stride = 2; + fp.buf5551 = true; + break; + default: break; } } - RDCFile *rdc = RenderDoc::Inst().CreateRDC(RDCDriver::Vulkan, m_CapturedFrames.back().frameNumber, - jpgbuf, len, thwidth, thheight, FileType::JPG); - - SAFE_DELETE_ARRAY(jpgbuf); - SAFE_DELETE_ARRAY(thpixels); + RDCFile *rdc = + RenderDoc::Inst().CreateRDC(RDCDriver::Vulkan, m_CapturedFrames.back().frameNumber, fp); StreamWriter *captureWriter = NULL;