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
This commit is contained in:
Alex Vakulenko
2018-08-31 15:27:22 -07:00
committed by Baldur Karlsson
parent 3233afd390
commit 264b296b7d
7 changed files with 283 additions and 495 deletions
+149 -15
View File
@@ -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<byte> 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;
}
+24 -2
View File
@@ -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 <typename ProgressType>
@@ -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)
+31 -108
View File
@@ -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;
+28 -107
View File
@@ -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;
+17 -111
View File
@@ -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 <typename SerialiserType>
+2 -12
View File
@@ -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 &params, ResourceId fboOrigId, GLuint &fbo,
std::string bbname);
BackbufferImage *SaveBackbufferImage();
map<void *, BackbufferImage *> m_BackbufferImages;
RenderDoc::FramePixels *SaveBackbufferImage();
map<void *, RenderDoc::FramePixels *> m_BackbufferImages;
void BuildGLExtensions();
void BuildGLESExtensions();
+32 -140
View File
@@ -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;