From d1eb71966e8c349e5abbad0504fee8a16f477039 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Tue, 24 Sep 2024 13:22:45 -0700 Subject: [PATCH] Update DirectXTexSwizzle.cpp More code review --- DirectXTex/DirectXTexSwizzle.cpp | 287 +++++++++++++++++++++++++------ 1 file changed, 238 insertions(+), 49 deletions(-) diff --git a/DirectXTex/DirectXTexSwizzle.cpp b/DirectXTex/DirectXTexSwizzle.cpp index 00ce5149..98d51ee8 100644 --- a/DirectXTex/DirectXTexSwizzle.cpp +++ b/DirectXTex/DirectXTexSwizzle.cpp @@ -15,6 +15,7 @@ using namespace DirectX; using namespace DirectX::Internal; + namespace { #ifdef __AVX2__ @@ -23,7 +24,7 @@ namespace #else // N3864 - A constexpr bitwise operations library for C++ // https://github.com/fmatthew5876/stdcxx-bitops - uint32_t deposit_bits(uint32_t val, uint32_t mask) + uint32_t deposit_bits(uint32_t val, int mask) { uint32_t res = 0; for (uint32_t bb = 1; mask != 0; bb += bb) @@ -36,7 +37,8 @@ namespace } return res; } - uint32_t extract_bits(uint32_t val, uint32_t mask) + + uint32_t extract_bits(uint32_t val, int mask) { uint32_t res = 0; for (uint32_t bb = 1; mask !=0; bb += bb) @@ -50,70 +52,166 @@ namespace return res; } #endif + +#if defined(_M_X64) || defined(_M_ARM64) + constexpr size_t MAX_TEXTURE_SIZE = 16384u * 16384u * 16u; // D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION is 16384 +#else + constexpr size_t MAX_TEXTURE_SIZE = UINT32_MAX; +#endif } +//------------------------------------------------------------------------------------- +// 2D z-order curve +//------------------------------------------------------------------------------------- +namespace +{ + constexpr uint16_t STANDARD_SWIZZLE_MASK_8 = 0b1010101000001111; + constexpr uint16_t STANDARD_SWIZZLE_MASK_16 = 0b1010101010001111; + constexpr uint16_t STANDARD_SWIZZLE_MASK_32 = 0b1010101010001111; + constexpr uint16_t STANDARD_SWIZZLE_MASK_64 = 0b1010101011001111; + constexpr uint16_t STANDARD_SWIZZLE_MASK_128 = 0b1010101011001111; + + inline int GetSwizzleMask(size_t bytesPerPixel) noexcept + { + switch(bytesPerPixel) + { + case 1: return STANDARD_SWIZZLE_MASK_8; + case 2: return STANDARD_SWIZZLE_MASK_16; + case 8: return STANDARD_SWIZZLE_MASK_64; + case 16: return STANDARD_SWIZZLE_MASK_128; + default: return STANDARD_SWIZZLE_MASK_32; + } + } +} + _Use_decl_annotations_ -HRESULT DirectX::StandardSwizzle(const Image& srcImage, bool toSwizzle, ScratchImage& result) noexcept +HRESULT DirectX::StandardSwizzle( + const Image& srcImage, + bool toSwizzle, + ScratchImage& result) noexcept { - if (!IsValid(srcImage.format)) + if (srcImage.height == 1) + { + // Standard Swizzle doesn't apply to 1D textures. return E_INVALIDARG; + } - if (IsTypeless(srcImage.format) || IsPlanar(srcImage.format) || IsPalettized(srcImage.format)) + if (IsPlanar(srcImage.format) || IsPalettized(srcImage.format) || (srcImage.format == DXGI_FORMAT_R1_UNORM)) return HRESULT_E_NOT_SUPPORTED; + if (srcImage.rowPitch > UINT32_MAX || srcImage.slicePitch > UINT32_MAX) + return HRESULT_E_ARITHMETIC_OVERFLOW; + HRESULT hr = result.Initialize2D(srcImage.format, srcImage.width, srcImage.height, 1, 1); if (FAILED(hr)) return hr; + const bool isCompressed = IsCompressed(srcImage.format); + const size_t bytesPerPixel = isCompressed ? BytesPerBlock(srcImage.format) : (BitsPerPixel(srcImage.format) / 8); + if (!bytesPerPixel) + { + result.Release(); + return E_FAIL; + } + + const size_t height = isCompressed ? (srcImage.height + 3) / 4 : srcImage.height; + const size_t width = isCompressed ? (srcImage.width + 3) / 4 : srcImage.width; + + if ((width > UINT32_MAX) || (height > UINT32_MAX)) + return E_INVALIDARG; + const uint8_t* sptr = srcImage.pixels; if (!sptr) + { + result.Release(); return E_POINTER; + } - uint8_t* dptr = result.GetImages()[0].pixels; + uint8_t* dptr = result.GetPixels(); if (!dptr) + { + result.Release(); return E_POINTER; + } - size_t bytesPerPixel = BitsPerPixel(srcImage.format) / 8; - - uint32_t xBytesMask = 0b1010101010101010; - uint32_t yBytesMask = 0b0101010101010101; - - size_t height = IsCompressed(srcImage.format) ? (srcImage.height + 3) / 4 : srcImage.height; - size_t width = IsCompressed(srcImage.format) ? (srcImage.width + 3) / 4 : srcImage.width; + const int xBytesMask = GetSwizzleMask(bytesPerPixel); + const size_t maxOffset = result.GetPixelsSize(); if (toSwizzle) { - size_t rowPitch = srcImage.rowPitch; - for (size_t y = 0; y < height; y++) + // row-major to z-order curve + const size_t rowPitch = srcImage.rowPitch; + const uint8_t* endPtr = sptr + (rowPitch * height); + for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; x++) + if (sptr >= endPtr) { - uint32_t swizzleIndex = deposit_bits(x, xBytesMask) + deposit_bits(y, yBytesMask); - size_t swizzleOffset = swizzleIndex * bytesPerPixel; + result.Release(); + return E_FAIL; + } - size_t rowMajorOffset = y * rowPitch + x * bytesPerPixel; + const uint8_t* sourcePixelPointer = sptr; + for (size_t x = 0; x < width; ++x) + { + const uint32_t swizzleIndex = deposit_bits(static_cast(x), xBytesMask) + deposit_bits(static_cast(y), ~xBytesMask); + const size_t swizzleOffset = swizzleIndex * bytesPerPixel; + if (swizzleOffset >= maxOffset) + { + result.Release(); + return E_UNEXPECTED; + } - const uint8_t* sourcePixelPointer = sptr + rowMajorOffset; uint8_t* destPixelPointer = dptr + swizzleOffset; memcpy(destPixelPointer, sourcePixelPointer, bytesPerPixel); + + sourcePixelPointer += bytesPerPixel; } + + sptr += rowPitch; } } else { - size_t rowPitch = result.GetImages()[0].rowPitch; - for (size_t swizzleIndex = 0; swizzleIndex < (width * height); swizzleIndex++) + // z-order curve to row-major + const size_t rowPitch = result.GetImages()[0].rowPitch; + + const uint64_t totalPixels = static_cast(width) * static_cast(height); + if (totalPixels > UINT32_MAX) { - size_t swizzleOffset = swizzleIndex * bytesPerPixel; + result.Release(); + return HRESULT_E_ARITHMETIC_OVERFLOW; + } + + const uint64_t totalDataSize = totalPixels * static_cast(bytesPerPixel); + if (totalDataSize > MAX_TEXTURE_SIZE) + { + result.Release(); + return HRESULT_E_ARITHMETIC_OVERFLOW; + } + + const uint8_t* endPtr = sptr + static_cast(totalDataSize); + for (size_t swizzleIndex = 0; swizzleIndex < static_cast(totalPixels); ++swizzleIndex) + { + if (sptr >= endPtr) + { + result.Release(); + return E_FAIL; + } uint32_t destX = extract_bits(swizzleIndex, xBytesMask); - uint32_t destY = extract_bits(swizzleIndex, yBytesMask); + uint32_t destY = extract_bits(swizzleIndex, ~xBytesMask); + size_t rowMajorOffset = destY * rowPitch + destX * bytesPerPixel; + if (rowMajorOffset >= maxOffset) + { + result.Release(); + return E_UNEXPECTED; + } - const uint8_t* sourcePixelPointer = sptr + swizzleOffset; uint8_t* destPixelPointer = dptr + rowMajorOffset; - memcpy(destPixelPointer, sourcePixelPointer, bytesPerPixel); + memcpy(destPixelPointer, sptr, bytesPerPixel); + sptr += bytesPerPixel; } } @@ -121,26 +219,40 @@ HRESULT DirectX::StandardSwizzle(const Image& srcImage, bool toSwizzle, ScratchI } _Use_decl_annotations_ -HRESULT DirectX::StandardSwizzle(const Image* srcImages, size_t nimages, const TexMetadata& metadata, bool toSwizzle, ScratchImage& result) noexcept +HRESULT DirectX::StandardSwizzle( + const Image* srcImages, + size_t nimages, + const TexMetadata& metadata, + bool toSwizzle, + ScratchImage& result) noexcept { - HRESULT hr = result.Initialize2D(srcImages[0].format, srcImages[0].width, srcImages[0].height, nimages, 1); - - if (!srcImages || !nimages || !IsValid(metadata.format) || nimages > metadata.mipLevels || !result.GetImages()) + if (!srcImages || !nimages || (metadata.dimension != TEX_DIMENSION_TEXTURE2D)) return E_INVALIDARG; - if (metadata.IsVolumemap() || IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format)) + if (IsPlanar(metadata.format) || IsPalettized(metadata.format) || (metadata.format == DXGI_FORMAT_R1_UNORM)) return HRESULT_E_NOT_SUPPORTED; - if (srcImages[0].format != metadata.format || srcImages[0].width != metadata.width || srcImages[0].height != metadata.height) + HRESULT hr = result.Initialize(metadata); + if (FAILED(hr)) + return hr; + + if (nimages != result.GetImageCount()) { - // Base image must be the same format, width, and height + result.Release(); return E_FAIL; } - size_t height = IsCompressed(metadata.format) ? (metadata.height + 3) / 4 : metadata.height; - size_t width = IsCompressed(metadata.format) ? (metadata.width + 3) / 4 : metadata.width; + const bool isCompressed = IsCompressed(metadata.format); + const size_t bytesPerPixel = isCompressed ? BytesPerBlock(metadata.format) : (BitsPerPixel(metadata.format) / 8); + if (!bytesPerPixel) + { + result.Release(); + return E_FAIL; + } - for (size_t imageIndex = 0; imageIndex < nimages; imageIndex++) + const int xBytesMask = GetSwizzleMask(bytesPerPixel); + + for (size_t imageIndex = 0; imageIndex < nimages; ++imageIndex) { const uint8_t* sptr = srcImages[imageIndex].pixels; if (!sptr) @@ -157,6 +269,8 @@ HRESULT DirectX::StandardSwizzle(const Image* srcImages, size_t nimages, const T if (toSwizzle) { + // row-major to z-order curve + size_t height = size_t rowPitch = srcImages[imageIndex].rowPitch; for (size_t y = 0; y < height; y++) { @@ -175,6 +289,7 @@ HRESULT DirectX::StandardSwizzle(const Image* srcImages, size_t nimages, const T } else { + // z-order curve to row-major size_t rowPitch = result.GetImages()[imageIndex].rowPitch; for (size_t swizzleIndex = 0; swizzleIndex < (width * height); swizzleIndex++) { @@ -194,33 +309,106 @@ HRESULT DirectX::StandardSwizzle(const Image* srcImages, size_t nimages, const T return S_OK; } -_Use_decl_annotations_ -HRESULT DirectX::StandardSwizzle3D(const Image* srcImages, size_t depth, const TexMetadata& metadata, bool toSwizzle, ScratchImage& result) noexcept + +//------------------------------------------------------------------------------------- +// 3D z-order curve +//------------------------------------------------------------------------------------- +namespace { - HRESULT hr = result.Initialize3D(srcImages[0].format, srcImages[0].width, srcImages[0].height, depth, 1); + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_X_8 = 0b1001000000001111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_X_16 = 0b1001000000001111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_X_32 = 0b1001001000001111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_X_64 = 0b1001001100001111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_X_128 = 0b1001001100001111; + + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Y_8 = 0b0100101000110000; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Y_16 = 0b0100101000110001; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Y_32 = 0b0100100100110011; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Y_64 = 0b0100100000110111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Y_128 = 0b0100100000111111; + + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Z_8 = 0b0010010111000000; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Z_16 = 0b0010010111000001; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Z_32 = 0b0010010011000011; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Z_64 = 0b0010010011000111; + constexpr uint16_t VOLUME_STANDARD_SWIZZLE_Z_128 = 0b0010010011001111; + + inline int GetSwizzleMask3D_X(size_t bytesPerPixel) noexcept + { + switch(bytesPerPixel) + { + case 1: return VOLUME_STANDARD_SWIZZLE_X_8; + case 2: return VOLUME_STANDARD_SWIZZLE_X_16; + case 8: return VOLUME_STANDARD_SWIZZLE_X_64; + case 16: return VOLUME_STANDARD_SWIZZLE_X_128; + default: return VOLUME_STANDARD_SWIZZLE_X_32; + } + } - if (!srcImages || !depth || !IsValid(metadata.format) || depth > metadata.depth || !result.GetImages()) + inline int GetSwizzleMask3D_Y(size_t bytesPerPixel) noexcept + { + switch(bytesPerPixel) + { + case 1: return VOLUME_STANDARD_SWIZZLE_Y_8; + case 2: return VOLUME_STANDARD_SWIZZLE_Y_16; + case 8: return VOLUME_STANDARD_SWIZZLE_Y_64; + case 16: return VOLUME_STANDARD_SWIZZLE_Y_128; + default: return VOLUME_STANDARD_SWIZZLE_Y_32; + } + } + + inline int GetSwizzleMask3D_Z(size_t bytesPerPixel) noexcept + { + switch(bytesPerPixel) + { + case 1: return VOLUME_STANDARD_SWIZZLE_Z_8; + case 2: return VOLUME_STANDARD_SWIZZLE_Z_16; + case 8: return VOLUME_STANDARD_SWIZZLE_Z_64; + case 16: return VOLUME_STANDARD_SWIZZLE_Z_128; + default: return VOLUME_STANDARD_SWIZZLE_Z_32; + } + } +} + +_Use_decl_annotations_ +HRESULT DirectX::StandardSwizzle3D( + const Image* srcImages, + size_t depth, + const TexMetadata& metadata, + bool toSwizzle, + ScratchImage& result) noexcept +{ + if (!srcImages || !depth || (metadata.dimension != TEX_DIMENSION_TEXTURE3D)) return E_INVALIDARG; - if (metadata.IsVolumemap() || IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format)) + if (IsPlanar(metadata.format) || IsPalettized(metadata.format) || (metadata.format == DXGI_FORMAT_R1_UNORM)) return HRESULT_E_NOT_SUPPORTED; - if (srcImages[0].format != metadata.format || srcImages[0].width != metadata.width || srcImages[0].height != metadata.height) + HRESULT hr = result.Initialize(metadata); + if (FAILED(hr)) + return hr; + + if ((depth * metadata.mipLevels) != result.GetImageCount()) { - // Base image must be the same format, width, and height + result.Release(); return E_FAIL; } - size_t height = IsCompressed(metadata.format) ? (metadata.height + 3) / 4 : metadata.height; - size_t width = IsCompressed(metadata.format) ? (metadata.width + 3) / 4 : metadata.width; + const bool isCompressed = IsCompressed(metadata.format); + const size_t bytesPerPixel = isCompressed ? BytesPerBlock(metadata.format) : (BitsPerPixel(metadata.format) / 8); + if (!bytesPerPixel) + { + result.Release(); + return E_FAIL; + } - size_t bytesPerPixel = BitsPerPixel(srcImages[0].format) / 8; - uint32_t xBytesMask = 0b1001001001001001; - uint32_t yBytesMask = 0b0100100100100100; - uint32_t zBytesMask = 0b0010010010010010; + const int xBytesMask = GetSwizzleMask3D_X(metadata.format); + const int yBytesMask = GetSwizzleMask3D_Y(metadata.format); + const int zBytesMask = GetSwizzleMask3D_Z(metadata.format); if (toSwizzle) { + // row-major to z-order curve const Image* destImages = result.GetImages(); for (size_t z = 0; z < depth; z++) { @@ -252,6 +440,7 @@ HRESULT DirectX::StandardSwizzle3D(const Image* srcImages, size_t depth, const T } else { + // z-order curve to row-major const Image* destImages = result.GetImages(); for (size_t z = 0; z < depth; z++) {