From c45023236a3771615ae12d9d8aa430ab4ceca438 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 17 Oct 2018 12:51:53 +0100 Subject: [PATCH] Perform image processing at 32bpp for HDR/EXR/DDS outputs. Closes #1103 * When flattening array slices or remapping to a cube cruciform, we can remap to RGBA32 when the output format is itself greater than 8bpp, to ensure we don't lose quality before output. --- renderdoc/replay/replay_controller.cpp | 128 +++++++++++++++++-------- 1 file changed, 87 insertions(+), 41 deletions(-) diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index cd2c4380c..9b85c6162 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -790,20 +790,41 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path td.format.type == ResourceFormatType::ASTC) downcast = true; - // for DDS don't downcast, for non-HDR always downcast if we're not already RGBA8 unorm - // for HDR&EXR we can convert from most regular types as well as 10.10.10.2 and 11.11.10 - if((sd.destType != FileType::DDS && sd.destType != FileType::HDR && sd.destType != FileType::EXR && - (td.format.compByteWidth != 1 || td.format.compCount != 4 || - td.format.compType != CompType::UNorm || td.format.bgraOrder)) || - downcast || (sd.destType != FileType::DDS && td.format.Special() && - td.format.type != ResourceFormatType::R10G10B10A2 && - td.format.type != ResourceFormatType::R11G11B10)) - { + // for non-HDR always downcast if we're not already RGBA8 unorm + if(sd.destType != FileType::DDS && sd.destType != FileType::HDR && sd.destType != FileType::EXR && + (td.format.compByteWidth != 1 || td.format.compCount != 4 || + td.format.compType != CompType::UNorm || td.format.bgraOrder)) downcast = true; - td.format.compByteWidth = 1; - td.format.compCount = 4; - td.format.compType = CompType::UNorm; - td.format.type = ResourceFormatType::Regular; + + // for HDR & EXR we can convert from most regular types as well as 10.10.10.2 and 11.11.10 + if(sd.destType != FileType::DDS && td.format.Special() && + td.format.type != ResourceFormatType::R10G10B10A2 && + td.format.type != ResourceFormatType::R11G11B10) + downcast = true; + + // if we're downcasting, pick either RGBA8 or RGBA32 to downcast to + RemapTexture remap = RemapTexture::NoRemap; + + if(downcast) + { + // if the source and destination are more than 1 byte per component, remap to RGBA32 + if(td.format.compByteWidth > 1 && (sd.destType == FileType::DDS || + sd.destType == FileType::HDR || sd.destType == FileType::EXR)) + { + remap = RemapTexture::RGBA32; + td.format.compByteWidth = 4; + td.format.compCount = 4; + td.format.compType = CompType::Float; + td.format.type = ResourceFormatType::Regular; + } + else + { + remap = RemapTexture::RGBA8; + td.format.compByteWidth = 1; + td.format.compCount = 4; + td.format.compType = CompType::UNorm; + td.format.type = ResourceFormatType::Regular; + } } uint32_t rowPitch = 0; @@ -868,7 +889,7 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path params.forDiskSave = true; params.typeHint = sd.typeHint; params.resolve = resolveSamples; - params.remap = downcast ? RemapTexture::RGBA8 : RemapTexture::NoRemap; + params.remap = remap; params.blackPoint = sd.comp.blackPoint; params.whitePoint = sd.comp.whitePoint; @@ -940,8 +961,9 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path } } - // should have been handled above, but verify incoming data is RGBA8 - if(sd.slice.slicesAsGrid && td.format.compByteWidth == 1 && td.format.compCount == 4) + // should have been handled above, but verify incoming data is RGBA8 or RGBA32 + if(sd.slice.slicesAsGrid && (td.format.compByteWidth == 1 || td.format.compByteWidth == 4) && + td.format.compCount == 4 && !td.format.Special()) { uint32_t sliceWidth = td.width; uint32_t sliceHeight = td.height; @@ -953,9 +975,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path td.width *= sd.slice.sliceGridWidth; td.height *= sliceGridHeight; - byte *combinedData = new byte[td.width * td.height * td.format.compCount]; + uint32_t pixelStride = td.format.compCount * td.format.compByteWidth; - memset(combinedData, 0, td.width * td.height * td.format.compCount); + byte *combinedData = new byte[td.width * td.height * pixelStride]; + + memset(combinedData, 0, td.width * td.height * pixelStride); for(size_t i = 0; i < subdata.size(); i++) { @@ -969,10 +993,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path { for(uint32_t x = 0; x < sliceWidth; x++) { - uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * 4 + 0]; - uint32_t *dstpix = (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * 4 + 0]; + uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * pixelStride + 0]; + uint32_t *dstpix = + (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * pixelStride + 0]; - *dstpix = *srcpix; + memcpy(dstpix, srcpix, pixelStride); } } @@ -984,9 +1009,9 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path rowPitch = td.width * 4; } - // should have been handled above, but verify incoming data is RGBA8 and 6 slices - if(sd.slice.cubeCruciform && td.format.compByteWidth == 1 && td.format.compCount == 4 && - subdata.size() == 6) + // should have been handled above, but verify incoming data is RGBA8 or RGBA32 and 6 slices + if(sd.slice.cubeCruciform && (td.format.compByteWidth == 1 || td.format.compByteWidth == 4) && + td.format.compCount == 4 && !td.format.Special() && subdata.size() == 6) { uint32_t sliceWidth = td.width; uint32_t sliceHeight = td.height; @@ -994,9 +1019,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path td.width *= 4; td.height *= 3; - byte *combinedData = new byte[td.width * td.height * td.format.compCount]; + uint32_t pixelStride = td.format.compCount * td.format.compByteWidth; - memset(combinedData, 0, td.width * td.height * td.format.compCount); + byte *combinedData = new byte[td.width * td.height * pixelStride]; + + memset(combinedData, 0, td.width * td.height * pixelStride); /* Y X=0 1 2 3 @@ -1025,10 +1052,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path { for(uint32_t x = 0; x < sliceWidth; x++) { - uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * 4 + 0]; - uint32_t *dstpix = (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * 4 + 0]; + uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * pixelStride + 0]; + uint32_t *dstpix = + (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * pixelStride + 0]; - *dstpix = *srcpix; + memcpy(dstpix, srcpix, pixelStride); } } @@ -1045,24 +1073,42 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path // if we want a grayscale image of one channel, splat it across all channels // and set alpha to full if(sd.channelExtract >= 0 && td.format.type == ResourceFormatType::Regular && - td.format.compByteWidth == 1 && (uint32_t)sd.channelExtract < td.format.compCount) + (td.format.compByteWidth == 1 || td.format.compByteWidth == 4) && + (uint32_t)sd.channelExtract < td.format.compCount) { - uint32_t cc = td.format.compCount; + uint32_t pixelStride = td.format.compCount * td.format.compByteWidth; + uint32_t compWidth = td.format.compByteWidth; + uint32_t compCount = td.format.compCount; + + uint32_t val = 0; + uint32_t max = ~0U; for(uint32_t y = 0; y < td.height; y++) { for(uint32_t x = 0; x < td.width; x++) { - subdata[0][(y * td.width + x) * cc + 0] = - subdata[0][(y * td.width + x) * cc + sd.channelExtract]; - if(cc >= 2) - subdata[0][(y * td.width + x) * cc + 1] = - subdata[0][(y * td.width + x) * cc + sd.channelExtract]; - if(cc >= 3) - subdata[0][(y * td.width + x) * cc + 2] = - subdata[0][(y * td.width + x) * cc + sd.channelExtract]; - if(cc >= 4) - subdata[0][(y * td.width + x) * cc + 3] = 255; + memcpy(&val, &subdata[0][(y * td.width + x) * pixelStride + sd.channelExtract * compWidth], + td.format.compByteWidth); + + switch(compCount) + { + case 4: + memcpy(&subdata[0][(y * td.width + x) * pixelStride + 3 * compWidth], &max, + td.format.compByteWidth); + // deliberate fallthrough + case 3: + memcpy(&subdata[0][(y * td.width + x) * pixelStride + 2 * compWidth], &val, + td.format.compByteWidth); + // deliberate fallthrough + case 2: + memcpy(&subdata[0][(y * td.width + x) * pixelStride + 1 * compWidth], &val, + td.format.compByteWidth); + // deliberate fallthrough + case 1: + memcpy(&subdata[0][(y * td.width + x) * pixelStride + 0 * compWidth], &val, + td.format.compByteWidth); + break; + } } } }