mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
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:
committed by
Baldur Karlsson
parent
3233afd390
commit
264b296b7d
+149
-15
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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<void *, BackbufferImage *> m_BackbufferImages;
|
||||
RenderDoc::FramePixels *SaveBackbufferImage();
|
||||
map<void *, RenderDoc::FramePixels *> m_BackbufferImages;
|
||||
|
||||
void BuildGLExtensions();
|
||||
void BuildGLESExtensions();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user