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;