From f622ac36d677489abf786fb9c0697a009303ee99 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 18 May 2020 11:04:06 +0100 Subject: [PATCH] Standardise layout of packed texture formats on disk/network * We preserve each API's interpretation of bit order for packed formats like RGBA4 or R5G6B5 when displaying the raw data in the UI, but when we need to proxy it or save to disk, we always transform to D3D's order as standard. * This allows us to proxy them reliably because we always have a standard bit order and APIs that need a different order transform when fetching data to the standard format, or setting proxy data from the standard format. --- renderdoc/api/replay/data_types.h | 8 ++ renderdoc/core/core.cpp | 8 +- renderdoc/core/replay_proxy.cpp | 1 + renderdoc/driver/d3d11/d3d11_replay.cpp | 10 --- renderdoc/driver/gl/gl_common.cpp | 24 ++++- renderdoc/driver/gl/gl_replay.cpp | 110 +++++++++++++++++++---- renderdoc/driver/vulkan/vk_replay.cpp | 30 +++++++ renderdoc/driver/vulkan/vk_resources.cpp | 13 +-- renderdoc/maths/formatpacking.cpp | 83 ++++++++++++++--- renderdoc/maths/formatpacking.h | 17 ++-- renderdoc/replay/replay_controller.cpp | 1 + renderdoc/replay/replay_driver.cpp | 1 + renderdoc/replay/replay_driver.h | 8 ++ util/test/demos/gl/gl_texture_zoo.cpp | 7 ++ util/test/demos/texture_zoo.cpp | 10 +-- util/test/demos/vk/vk_texture_zoo.cpp | 7 +- util/test/rdtest/shared/Texture_Zoo.py | 12 +-- 17 files changed, 279 insertions(+), 71 deletions(-) diff --git a/renderdoc/api/replay/data_types.h b/renderdoc/api/replay/data_types.h index b765c8dfc..75b4e0a06 100644 --- a/renderdoc/api/replay/data_types.h +++ b/renderdoc/api/replay/data_types.h @@ -203,6 +203,14 @@ struct ResourceFormat )"); DOCUMENT(R"(:return: ``True`` if the components are to be read in ``BGRA`` order. + +.. note:: + The convention is that components are in RGBA order. Whether that means first byte to last byte, + or in bit-packed formats red in the lowest bits. + + With BGRA order this means blue is in the first byte/lowest bits, but alpha is still always + expected in the last byte/uppermost bits. + :rtype: ``bool`` )"); bool BGRAOrder() const { return (flags & ResourceFormat_BGRA) != 0; } diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index 17b6cf6d7..26543967f 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -1029,17 +1029,17 @@ void RenderDoc::ResamplePixels(const FramePixels &in, RDCThumb &out) { uint16_t *src565 = (uint16_t *)src; Vec3f unorm = ConvertFromB5G6R5(*src565); - dst[0] = (byte)(unorm.z * 255.0f); + dst[0] = (byte)(unorm.x * 255.0f); dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.x * 255.0f); + dst[2] = (byte)(unorm.z * 255.0f); } else if(in.buf5551) { uint16_t *src5551 = (uint16_t *)src; Vec4f unorm = ConvertFromB5G5R5A1(*src5551); - dst[0] = (byte)(unorm.z * 255.0f); + dst[0] = (byte)(unorm.x * 255.0f); dst[1] = (byte)(unorm.y * 255.0f); - dst[2] = (byte)(unorm.x * 255.0f); + dst[2] = (byte)(unorm.z * 255.0f); } else if(in.bgra) { diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 2c522c028..04910f525 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -2389,6 +2389,7 @@ void ReplayProxy::EnsureTexCached(ResourceId &texid, CompType &typeCast, const S GetTextureDataParams params = proxy.params; params.typeCast = typeCast; + params.standardLayout = true; #if ENABLED(TRANSFER_RESOURCE_CONTENTS_DELTAS) CacheTextureData(texid, s, params); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 6a28f9eb6..4734c79a8 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -3641,16 +3641,6 @@ void D3D11Replay::SetProxyTextureData(ResourceId texid, const Subresource &sub, bool D3D11Replay::IsTextureSupported(const TextureDescription &tex) { - // these formats are inconsistently laid out between APIs, always remap - switch(tex.format.type) - { - case ResourceFormatType::R4G4: - case ResourceFormatType::R4G4B4A4: - case ResourceFormatType::R5G6B5: - case ResourceFormatType::R5G5B5A1: return false; - default: break; - } - DXGI_FORMAT f = MakeDXGIFormat(tex.format); if(f == DXGI_FORMAT_UNKNOWN) diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index 9daa54995..ef3c37846 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -2039,11 +2039,13 @@ ResourceFormat MakeResourceFormat(GLenum target, GLenum fmt) ret.type = ResourceFormatType::R5G6B5; ret.compType = CompType::UNorm; ret.compCount = 3; + ret.SetBGRAOrder(true); break; case eGL_RGB5_A1: ret.type = ResourceFormatType::R5G5B5A1; ret.compType = CompType::UNorm; ret.compCount = 4; + ret.SetBGRAOrder(true); break; case eGL_RGB9_E5: ret.type = ResourceFormatType::R9G9B9E5; @@ -2054,6 +2056,7 @@ ResourceFormat MakeResourceFormat(GLenum target, GLenum fmt) ret.type = ResourceFormatType::R4G4B4A4; ret.compType = CompType::UNorm; ret.compCount = 4; + ret.SetBGRAOrder(true); break; case eGL_RGB10_A2: case eGL_RGB10_A2UI: @@ -2268,10 +2271,25 @@ GLenum MakeGLFormat(ResourceFormat fmt) ret = eGL_RGB10_A2UI; break; case ResourceFormatType::R11G11B10: ret = eGL_R11F_G11F_B10F; break; - case ResourceFormatType::R5G6B5: ret = eGL_RGB565; break; - case ResourceFormatType::R5G5B5A1: ret = eGL_RGB5_A1; break; + case ResourceFormatType::R5G6B5: + // only support bgra order + if(fmt.BGRAOrder()) + return eGL_RGB565; + else + return eGL_NONE; + case ResourceFormatType::R5G5B5A1: + // only support bgra order + if(fmt.BGRAOrder()) + return eGL_RGB5_A1; + else + return eGL_NONE; case ResourceFormatType::R9G9B9E5: ret = eGL_RGB9_E5; break; - case ResourceFormatType::R4G4B4A4: ret = eGL_RGBA4; break; + case ResourceFormatType::R4G4B4A4: + // only support bgra order + if(fmt.BGRAOrder()) + return eGL_RGBA4; + else + return eGL_NONE; case ResourceFormatType::D24S8: ret = eGL_DEPTH24_STENCIL8; break; case ResourceFormatType::D32S8: ret = eGL_DEPTH32F_STENCIL8; break; case ResourceFormatType::D16S8: return eGL_NONE; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index f885185a2..2ff35be02 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2319,7 +2319,11 @@ void GLReplay::GetTextureData(ResourceId tex, const Subresource &sub, else if(params.remap == RemapTexture::RGBA32) remapFormat = eGL_RGBA32F; - remapFormat = GetViewCastedFormat(remapFormat, BaseRemapType(params.typeCast)); + CompType typeCast = params.typeCast; + if(typeCast == CompType::Typeless && IsSRGBFormat(intFormat)) + typeCast = CompType::UNormSRGB; + + remapFormat = GetViewCastedFormat(remapFormat, BaseRemapType(typeCast)); if(intFormat != remapFormat) { @@ -2723,19 +2727,50 @@ void GLReplay::GetTextureData(ResourceId tex, const Subresource &sub, drv.glGetTexImage(target, (GLint)s.mip, fmt, type, data.data()); } - // GL puts D24 in the top bits (whether or not there's stencil). We choose to standardise it - // to be in the low bits, so swizzle here. for D24 with no stencil, the stencil bits are - // undefined so we can move them around and it means nothing. - if(intFormat == eGL_DEPTH24_STENCIL8 || intFormat == eGL_DEPTH_COMPONENT24) + if(params.standardLayout) { - uint32_t *ptr = (uint32_t *)data.data(); - - for(GLsizei y = 0; y < height; y++) + // GL puts D24 in the top bits (whether or not there's stencil). We choose to standardise it + // to be in the low bits, so swizzle here. for D24 with no stencil, the stencil bits are + // undefined so we can move them around and it means nothing. + if(intFormat == eGL_DEPTH24_STENCIL8 || intFormat == eGL_DEPTH_COMPONENT24) { - for(GLsizei x = 0; x < width; x++) + uint32_t *ptr = (uint32_t *)data.data(); + + for(GLsizei z = 0; z < depth; z++) { - const uint32_t val = *ptr; - *ptr = (val >> 8) | ((val & 0xff) << 24); + for(GLsizei y = 0; y < height; y++) + { + for(GLsizei x = 0; x < width; x++) + { + const uint32_t val = *ptr; + *ptr = (val >> 8) | ((val & 0xff) << 24); + ptr++; + } + } + } + } + + // GL's RGBA4/RGB5A1 is BGRA order, but it puts alpha in the bottom bits where we expect it + // in the top + if(intFormat == eGL_RGBA4) + { + uint16_t *ptr = (uint16_t *)data.data(); + + for(size_t i = 0; i < data.size(); i += sizeof(uint16_t)) + { + const uint16_t val = *ptr; + *ptr = (val >> 4) | ((val & 0xf) << 12); + ptr++; + } + } + else if(intFormat == eGL_RGB5_A1) + { + uint16_t *ptr = (uint16_t *)data.data(); + + for(size_t i = 0; i < data.size(); i += sizeof(uint16_t)) + { + const uint16_t val = *ptr; + *ptr = (val >> 1) | ((val & 0x1) << 15); ptr++; } } @@ -3246,6 +3281,40 @@ void GLReplay::SetProxyTextureData(ResourceId texid, const Subresource &sub, byt data = swizzled.data(); } + // GL's RGBA4/RGB5A1 is BGRA order, but it puts alpha in the bottom bits where we expect it + // in the top + else if(texdetails.internalFormat == eGL_RGBA4) + { + const uint16_t *srcptr = (const uint16_t *)data; + swizzled.resize(dataSize); + uint16_t *dstptr = (uint16_t *)swizzled.data(); + + for(size_t i = 0; i < dataSize; i += 2) + { + const uint16_t val = *srcptr; + *dstptr = ((val & 0x0fff) << 4) | ((val & 0xf000) >> 12); + srcptr++; + dstptr++; + } + + data = swizzled.data(); + } + else if(texdetails.internalFormat == eGL_RGB5_A1) + { + const uint16_t *srcptr = (const uint16_t *)data; + swizzled.resize(dataSize); + uint16_t *dstptr = (uint16_t *)swizzled.data(); + + for(size_t i = 0; i < dataSize; i += 2) + { + const uint16_t val = *srcptr; + *dstptr = ((val & 0x7fff) << 1) | ((val & 0x8000) >> 12); + srcptr++; + dstptr++; + } + + data = swizzled.data(); + } if(target == eGL_TEXTURE_1D) { @@ -3312,8 +3381,13 @@ void GLReplay::SetProxyTextureData(ResourceId texid, const Subresource &sub, byt bool GLReplay::IsTextureSupported(const TextureDescription &tex) { - // these formats are inconsistently laid out between APIs, always remap if the remote API is - // different + // GL can't decide if these formats are BGRA or RGBA order. + // The bit order in memory for e.g. R4G4B4A4 is: + // 15 .. .. 0 + // R G B A + // + // but if you upload bits in that order with GL_RGBA it gets flipped. + // It's more reliable to report no support and force a remap switch(tex.format.type) { case ResourceFormatType::R4G4: @@ -3332,11 +3406,6 @@ bool GLReplay::IsTextureSupported(const TextureDescription &tex) if(tex.format.type == ResourceFormatType::A8) return false; - // BGRA is not accepted as an internal format in case of GL - // EXT_texture_format_BGRA8888 is required for creating BGRA proxy textures in case of GLES - if(tex.format.BGRAOrder()) - return IsGLES && HasExt[EXT_texture_format_BGRA8888]; - // don't support 1D/3D block compressed textures if(tex.dimension != 2 && (tex.format.type == ResourceFormatType::BC1 || tex.format.type == ResourceFormatType::BC2 || @@ -3357,6 +3426,11 @@ bool GLReplay::IsTextureSupported(const TextureDescription &tex) if(fmt == eGL_NONE) return false; + // BGRA is not accepted as an internal format in case of GL + // EXT_texture_format_BGRA8888 is required for creating BGRA proxy textures in case of GLES + if(fmt == eGL_BGRA8_EXT && (!IsGLES || !HasExt[EXT_texture_format_BGRA8888])) + return false; + GLenum target = eGL_TEXTURE_2D; switch(tex.type) diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index d215447e8..59f06f557 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -3628,6 +3628,36 @@ void VulkanReplay::GetTextureData(ResourceId tex, const Subresource &sub, else { memcpy(data.data(), pData, dataSize); + + // vulkan's bitpacking of some layouts puts alpha in the low bits, which is not our 'standard' + // layout and is not representable in our resource formats + if(params.standardLayout) + { + if(imCreateInfo.format == VK_FORMAT_R4G4B4A4_UNORM_PACK16 || + imCreateInfo.format == VK_FORMAT_B4G4R4A4_UNORM_PACK16) + { + uint16_t *ptr = (uint16_t *)data.data(); + + for(uint32_t i = 0; i < dataSize; i += sizeof(uint16_t)) + { + const uint16_t val = *ptr; + *ptr = (val >> 4) | ((val & 0xf) << 12); + ptr++; + } + } + else if(imCreateInfo.format == VK_FORMAT_R5G5B5A1_UNORM_PACK16 || + imCreateInfo.format == VK_FORMAT_B5G5R5A1_UNORM_PACK16) + { + uint16_t *ptr = (uint16_t *)data.data(); + + for(uint32_t i = 0; i < dataSize; i += sizeof(uint16_t)) + { + const uint16_t val = *ptr; + *ptr = (val >> 1) | ((val & 0x1) << 15); + ptr++; + } + } + } } vt->UnmapMemory(Unwrap(dev), readbackMem); diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 8355e2278..25bf83261 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -1811,9 +1811,10 @@ ResourceFormat MakeResourceFormat(VkFormat fmt) switch(fmt) { - case VK_FORMAT_B4G4R4A4_UNORM_PACK16: - case VK_FORMAT_B5G6R5_UNORM_PACK16: - case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: case VK_FORMAT_B8G8R8A8_UNORM: case VK_FORMAT_B8G8R8A8_SNORM: case VK_FORMAT_B8G8R8A8_USCALED: @@ -2710,14 +2711,14 @@ VkFormat MakeVkFormat(ResourceFormat fmt) break; case ResourceFormatType::R11G11B10: ret = VK_FORMAT_B10G11R11_UFLOAT_PACK32; break; case ResourceFormatType::R5G6B5: - ret = fmt.BGRAOrder() ? VK_FORMAT_B5G6R5_UNORM_PACK16 : VK_FORMAT_R5G6B5_UNORM_PACK16; + ret = fmt.BGRAOrder() ? VK_FORMAT_R5G6B5_UNORM_PACK16 : VK_FORMAT_B5G6R5_UNORM_PACK16; break; case ResourceFormatType::R5G5B5A1: - ret = fmt.BGRAOrder() ? VK_FORMAT_B5G5R5A1_UNORM_PACK16 : VK_FORMAT_R5G5B5A1_UNORM_PACK16; + ret = fmt.BGRAOrder() ? VK_FORMAT_R5G5B5A1_UNORM_PACK16 : VK_FORMAT_B5G5R5A1_UNORM_PACK16; break; case ResourceFormatType::R9G9B9E5: ret = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; break; case ResourceFormatType::R4G4B4A4: - ret = fmt.BGRAOrder() ? VK_FORMAT_B4G4R4A4_UNORM_PACK16 : VK_FORMAT_R4G4B4A4_UNORM_PACK16; + ret = fmt.BGRAOrder() ? VK_FORMAT_R4G4B4A4_UNORM_PACK16 : VK_FORMAT_B4G4R4A4_UNORM_PACK16; break; case ResourceFormatType::R4G4: ret = VK_FORMAT_R4G4_UNORM_PACK8; break; case ResourceFormatType::D16S8: ret = VK_FORMAT_D16_UNORM_S8_UINT; break; diff --git a/renderdoc/maths/formatpacking.cpp b/renderdoc/maths/formatpacking.cpp index 572694770..d5e9630b9 100644 --- a/renderdoc/maths/formatpacking.cpp +++ b/renderdoc/maths/formatpacking.cpp @@ -301,6 +301,9 @@ FloatVector ConvertComponents(const ResourceFormat &fmt, const byte *data) ret.y = v.y; ret.z = v.z; ret.w = v.w; + + if(fmt.BGRAOrder()) + std::swap(ret.x, ret.z); } else if(fmt.type == ResourceFormatType::R11G11B10) { @@ -312,25 +315,46 @@ FloatVector ConvertComponents(const ResourceFormat &fmt, const byte *data) else if(fmt.type == ResourceFormatType::R5G5B5A1) { Vec4f v = ConvertFromB5G5R5A1(*(const uint16_t *)data); - ret.x = v.z; + ret.x = v.x; ret.y = v.y; - ret.z = v.x; + ret.z = v.z; ret.w = v.w; + + // conversely we *expect* BGRA order for this format and the above conversion implicitly flips + // when bit-unpacking. So if the format wasn't BGRA order, flip it back + if(!fmt.BGRAOrder()) + std::swap(ret.x, ret.z); } else if(fmt.type == ResourceFormatType::R5G6B5) { Vec3f v = ConvertFromB5G6R5(*(const uint16_t *)data); - ret.x = v.z; + ret.x = v.x; ret.y = v.y; - ret.z = v.x; + ret.z = v.z; + + // conversely we *expect* BGRA order for this format and the above conversion implicitly flips + // when bit-unpacking. So if the format wasn't BGRA order, flip it back + if(!fmt.BGRAOrder()) + std::swap(ret.x, ret.z); } else if(fmt.type == ResourceFormatType::R4G4B4A4) { Vec4f v = ConvertFromB4G4R4A4(*(const uint16_t *)data); - ret.x = v.z; + ret.x = v.x; ret.y = v.y; - ret.z = v.x; + ret.z = v.z; ret.w = v.w; + + // conversely we *expect* BGRA order for this format and the above conversion implicitly flips + // when bit-unpacking. So if the format wasn't BGRA order, flip it back + if(!fmt.BGRAOrder()) + std::swap(ret.x, ret.z); + } + else if(fmt.type == ResourceFormatType::R4G4) + { + Vec4f v = ConvertFromR4G4(*(const uint8_t *)data); + ret.x = v.x; + ret.y = v.y; } else if(fmt.type == ResourceFormatType::R9G9B9E5) { @@ -338,9 +362,35 @@ FloatVector ConvertComponents(const ResourceFormat &fmt, const byte *data) ret.x = v.x; ret.y = v.y; ret.z = v.z; - RDCLOG("%x -> %f,%f,%f", *(const uint32_t *)data, v.x, v.y, v.z); } - else + else if(fmt.type == ResourceFormatType::D16S8) + { + uint32_t val = *(const uint32_t *)data; + ret.x = float(val & 0x00ffff) / 65535.0f; + ret.y = float((val & 0xff0000) >> 16) / 255.0f; + ret.z = 0.0f; + } + else if(fmt.type == ResourceFormatType::D24S8) + { + uint32_t val = *(const uint32_t *)data; + ret.x = float(val & 0x00ffffff) / 16777215.0f; + ret.y = float((val & 0xff000000) >> 24) / 255.0f; + ret.z = 0.0f; + } + else if(fmt.type == ResourceFormatType::D32S8) + { + struct ds + { + float f; + uint32_t s; + } val; + val = *(const ds *)data; + ret.x = val.f; + ret.y = float(val.s) / 255.0f; + ret.z = 0.0f; + } + else if(fmt.type == ResourceFormatType::Regular || fmt.type == ResourceFormatType::A8 || + fmt.type == ResourceFormatType::S8) { float *comp = &ret.x; @@ -394,9 +444,9 @@ FloatVector ConvertComponents(const ResourceFormat &fmt, const byte *data) const uint8_t *u8 = (const uint8_t *)data; uint32_t depth = 0; - depth |= uint32_t(u8[1]); - depth |= uint32_t(u8[2]) << 8; - depth |= uint32_t(u8[3]) << 16; + depth |= uint32_t(u8[0]); + depth |= uint32_t(u8[1]) << 8; + depth |= uint32_t(u8[2]) << 16; *comp = float(depth) / float(16777215.0f); } @@ -476,6 +526,17 @@ FloatVector ConvertComponents(const ResourceFormat &fmt, const byte *data) data += fmt.compByteWidth; } + if(fmt.type == ResourceFormatType::A8) + { + ret.w = ret.x; + ret.x = 0.0f; + } + else if(fmt.type == ResourceFormatType::S8) + { + ret.y = ret.x; + ret.x = 0.0f; + } + if(fmt.BGRAOrder()) std::swap(ret.x, ret.z); } diff --git a/renderdoc/maths/formatpacking.h b/renderdoc/maths/formatpacking.h index 41531b1a4..bffb488d1 100644 --- a/renderdoc/maths/formatpacking.h +++ b/renderdoc/maths/formatpacking.h @@ -79,20 +79,25 @@ uint32_t ConvertToR11G11B10(Vec3f data); inline Vec4f ConvertFromB5G5R5A1(uint16_t data) { - return Vec4f((float)((data >> 0) & 0x1f) / 31.0f, (float)((data >> 5) & 0x1f) / 31.0f, - (float)((data >> 10) & 0x1f) / 31.0f, ((data & 0x8000) > 0) ? 1.0f : 0.0f); + return Vec4f((float)((data >> 10) & 0x1f) / 31.0f, (float)((data >> 5) & 0x1f) / 31.0f, + (float)((data >> 0) & 0x1f) / 31.0f, ((data & 0x8000) > 0) ? 1.0f : 0.0f); } inline Vec3f ConvertFromB5G6R5(uint16_t data) { - return Vec3f((float)((data >> 0) & 0x1f) / 31.0f, (float)((data >> 5) & 0x3f) / 63.0f, - (float)((data >> 11) & 0x1f) / 31.0f); + return Vec3f((float)((data >> 11) & 0x1f) / 31.0f, (float)((data >> 5) & 0x3f) / 63.0f, + (float)((data >> 0) & 0x1f) / 31.0f); } inline Vec4f ConvertFromB4G4R4A4(uint16_t data) { - return Vec4f((float)((data >> 0) & 0xf) / 15.0f, (float)((data >> 4) & 0xf) / 15.0f, - (float)((data >> 8) & 0xf) / 15.0f, (float)((data >> 12) & 0xf) / 15.0f); + return Vec4f((float)((data >> 8) & 0xf) / 15.0f, (float)((data >> 4) & 0xf) / 15.0f, + (float)((data >> 0) & 0xf) / 15.0f, (float)((data >> 12) & 0xf) / 15.0f); +} + +inline Vec4f ConvertFromR4G4(uint8_t data) +{ + return Vec4f((float)((data >> 0) & 0xf) / 15.0f, (float)((data >> 4) & 0xf) / 15.0f, 0.0f, 0.0f); } Vec3f ConvertFromR9G9B9E5(uint32_t data); diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 4b0155b37..408d354d4 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -847,6 +847,7 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path GetTextureDataParams params; params.forDiskSave = true; + params.standardLayout = true; params.typeCast = sd.typeCast; params.resolve = resolveSamples; params.remap = remap; diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index ff2bc4043..5d6c33d5b 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -43,6 +43,7 @@ template void DoSerialise(SerialiserType &ser, GetTextureDataParams &el) { SERIALISE_MEMBER(forDiskSave); + SERIALISE_MEMBER(standardLayout); SERIALISE_MEMBER(typeCast); SERIALISE_MEMBER(resolve); SERIALISE_MEMBER(remap); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 5deddc66b..d97cb96c4 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -83,7 +83,15 @@ DECLARE_REFLECTION_ENUM(RemapTexture); struct GetTextureDataParams { + // this data is going to be saved to disk, so prepare it as needed. E.g. on GL flip Y order to + // match conventional axis for file formats. bool forDiskSave = false; + // this data is going to be transferred cross-API e.g. in replay proxying, so standardise bit + // layout of any packed formats where API conventions differ (mostly only RGBA4 or other awkward + // ones where our resource formats don't enumerate all possible iterations). Saving to disk is + // also standardised to ensure the data matches any format description we also write to the + // format. + bool standardLayout = false; CompType typeCast = CompType::Typeless; bool resolve = false; RemapTexture remap = RemapTexture::NoRemap; diff --git a/util/test/demos/gl/gl_texture_zoo.cpp b/util/test/demos/gl/gl_texture_zoo.cpp index 31f6cd640..0a340d4e9 100644 --- a/util/test/demos/gl/gl_texture_zoo.cpp +++ b/util/test/demos/gl/gl_texture_zoo.cpp @@ -848,11 +848,16 @@ void main() t.hasData = true; bool srgb = false; + bool bgr = false; switch(t.fmt.internalFormat) { // only need to handle renderable SRGB formats here case GL_SRGB8: case GL_SRGB8_ALPHA8: srgb = true; break; + case GL_BGRA8_EXT: + case GL_RGBA4: + case GL_RGB5_A1: + case GL_RGB565: bgr = true; default: break; } @@ -862,6 +867,8 @@ void main() flags |= 1; if(srgb) flags |= 2; + if(bgr) + flags |= 4; GLuint slices = t.isArray ? texSlices : 1u; GLuint mips = t.isMSAA || t.isRect ? 1u : texMips; diff --git a/util/test/demos/texture_zoo.cpp b/util/test/demos/texture_zoo.cpp index 4d04f04ed..e12b5600f 100644 --- a/util/test/demos/texture_zoo.cpp +++ b/util/test/demos/texture_zoo.cpp @@ -189,7 +189,7 @@ void MakeData(TexData &data, const TexConfig &cfg, Vec4i dimensions, uint32_t mi case TextureType::BC7: bc7 = true; break; case TextureType::R9G9B9E5: sharedExp = true; break; case TextureType::G4R4: nybblePattern = 0x12; break; - case TextureType::A4R4G4B4: nybblePattern = 0x1234; break; + case TextureType::A4R4G4B4: nybblePattern = 0x3214; break; case TextureType::R4G4B4A4: nybblePattern = 0x4321; break; case TextureType::R5G6B5: rgb5 = true; @@ -302,15 +302,15 @@ void MakeData(TexData &data, const TexConfig &cfg, Vec4i dimensions, uint32_t mi if(alphabitPlace == 0) { - encodedPixel |= uint16_t(rgb[2] * 31) << 0; + encodedPixel |= uint16_t(rgb[0] * 31) << 0; encodedPixel |= uint16_t(rgb[1] * 63) << 5; - encodedPixel |= uint16_t(rgb[0] * 31) << 11; + encodedPixel |= uint16_t(rgb[2] * 31) << 11; } else { - encodedPixel |= uint16_t(rgb[2] * 31) << 0; + encodedPixel |= uint16_t(rgb[0] * 31) << 0; encodedPixel |= uint16_t(rgb[1] * 31) << 5; - encodedPixel |= uint16_t(rgb[0] * 31) << 10; + encodedPixel |= uint16_t(rgb[2] * 31) << 10; if(alphabitPlace == 1) { diff --git a/util/test/demos/vk/vk_texture_zoo.cpp b/util/test/demos/vk/vk_texture_zoo.cpp index 92a2fd451..3c9cadbaf 100644 --- a/util/test/demos/vk/vk_texture_zoo.cpp +++ b/util/test/demos/vk/vk_texture_zoo.cpp @@ -1172,9 +1172,10 @@ void main() bool srgb = false, bgra = false; switch(t.fmt.viewFmt) { - case VK_FORMAT_B4G4R4A4_UNORM_PACK16: - case VK_FORMAT_B5G6R5_UNORM_PACK16: - case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: case VK_FORMAT_B8G8R8_UNORM: case VK_FORMAT_B8G8R8_SNORM: case VK_FORMAT_B8G8R8_USCALED: diff --git a/util/test/rdtest/shared/Texture_Zoo.py b/util/test/rdtest/shared/Texture_Zoo.py index 507a139d2..6165ae10e 100644 --- a/util/test/rdtest/shared/Texture_Zoo.py +++ b/util/test/rdtest/shared/Texture_Zoo.py @@ -15,7 +15,9 @@ class Texture_Zoo(): self.textures = {} self.controller: rd.ReplayController self.controller = None + self.pipeType = rd.GraphicsAPI.D3D11 self.opengl_mode = False + self.d3d_mode = False def sub(self, mip: int, slice: int, sample: int): if self.fake_msaa: @@ -70,6 +72,9 @@ class Texture_Zoo(): success: bool = self.controller.SaveTexture(save_data, path) if not success: + if self.d3d_mode: + raise rdtest.TestFailureException("Couldn't save DDS to {} on D3D.".format(self.filename)) + try: os.remove(path) except Exception: @@ -369,7 +374,9 @@ class Texture_Zoo(): def check_capture(self, capture_filename: str, controller: rd.ReplayController): self.controller = controller + self.pipeType = self.controller.GetAPIProperties().pipelineType self.opengl_mode = (self.controller.GetAPIProperties().pipelineType == rd.GraphicsAPI.OpenGL) + self.d3d_mode = rd.IsD3D(self.controller.GetAPIProperties().pipelineType) failed = False @@ -442,11 +449,6 @@ class Texture_Zoo(): ret: Tuple[rd.ReplayStatus, rd.ReplayController] = cap.OpenCapture(rd.ReplayOptions(), None) status, self.controller = ret - # Some packed formats can't be opened, allow that - if status == rd.ReplayStatus.ImageUnsupported and 'dds' in file.name: - rdtest.log.print("Couldn't open {} - unsupported".format(file.name)) - continue - if status != rd.ReplayStatus.Succeeded: rdtest.log.error("Couldn't open {}".format(file.name)) failed = True