[Fix] (MG_Util, DirectVulkan): canonical depth shadows with upload conversion

Depth textures previously raw-copied whatever the client handed over
into the Vulkan image, so any client format other than the image's exact
texel layout uploaded garbage (float DEPTH_COMPONENT data read as
16-bit words, GL_TEXTURE_1D/2D alike).

The shadow now has a defined canonical layout - unorm16 for
DEPTH_COMPONENT16, a full-scale unorm32 word for the 24/32-bit fixed
depths, float for DEPTH_COMPONENT32F - produced by the pixel-store
unpack converter (new DepthComponent channel mapping + UNorm32
component). GL_DEPTH_COMPONENT client data may also fill packed
depth-stencil internals (stencil half zero). The Vulkan uploader
converts shadow words to the image texel layout per aspect, and
X8_D24_UNORM falls back to D32_SFLOAT where optimal tiling lacks
support (lavapipe). texture_size_promotion.functional and
packed_depth_stencil.verify_copy_tex_image.* now pass.
This commit is contained in:
BZLZHH
2026-08-01 01:15:58 -04:00
parent f0c0211767
commit 22b749dd37
3 changed files with 167 additions and 7 deletions
+4 -1
View File
@@ -43,8 +43,11 @@ namespace MobileGL {
case TextureInternalFormat::SRGB8:
case TextureInternalFormat::RGB8I:
case TextureInternalFormat::RGB8UI:
case TextureInternalFormat::DepthComponent24:
return 3;
// Canonical depth shadow is a full 32-bit unorm word (see PixelStoreProcessor),
// converted at upload to the image's own 24/32-bit layout.
case TextureInternalFormat::DepthComponent24:
return 4;
case TextureInternalFormat::RGBA2:
case TextureInternalFormat::RGBA4:
@@ -95,6 +95,7 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
Int32,
Half,
Float32,
UNorm32, // 32-bit fixed-point depth shadow
};
struct InternalShadowLayout {
@@ -123,6 +124,21 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
Bool GetInternalShadowLayout(TextureInternalFormat internal, InternalShadowLayout& out) {
switch (internal) {
// Depth shadows follow TextureFormatProcessor::NormalizePixelFormat: 16-bit
// unorm for DEPTH_COMPONENT16, 32-bit unorm for the 24/32-bit fixed-point
// depths, float for DEPTH_COMPONENT32F.
case TextureInternalFormat::DepthComponent16:
out = {1, ShadowComponent::UNorm16, false};
return true;
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent:
out = {1, ShadowComponent::UNorm32, false};
return true;
case TextureInternalFormat::DepthComponent32F:
out = {1, ShadowComponent::Float32, false};
return true;
case TextureInternalFormat::R8:
case TextureInternalFormat::Red: out = {1, ShadowComponent::UNorm8, false}; return true;
case TextureInternalFormat::RG8:
@@ -299,8 +315,10 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
case TextureInputFormat::RGBAInteger: out = {{0, 1, 2, 3}, 4, true}; return true;
case TextureInputFormat::BGRA: out = {{2, 1, 0, 3}, 4, false}; return true;
case TextureInputFormat::BGRAInteger: out = {{2, 1, 0, 3}, 4, true}; return true;
// A depth value converts like a single normalized/float channel.
case TextureInputFormat::DepthComponent: out = {{0, -1, -1, -1}, 1, false}; return true;
default:
return false; // depth / stencil / unknown
return false; // stencil / packed depth-stencil / unknown
}
}
@@ -346,8 +364,7 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
out = isInteger ? ShadowComponent::Int16 : ShadowComponent::SNorm16;
return true;
case TexturePixelDataType::UnsignedInt:
if (!isInteger) return false; // no 32-bit normalized shadow layout
out = ShadowComponent::UInt32;
out = isInteger ? ShadowComponent::UInt32 : ShadowComponent::UNorm32;
return true;
case TexturePixelDataType::Int:
if (!isInteger) return false;
@@ -617,6 +634,12 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
case ShadowComponent::Float32:
Memcpy(dst, &v, sizeof(v));
break;
case ShadowComponent::UNorm32: {
const auto out = static_cast<Uint32>(
std::llround(static_cast<double>(std::clamp(v, 0.0f, 1.0f)) * 4294967295.0));
Memcpy(dst, &out, sizeof(out));
break;
}
default:
break; // integer components never reach the float encoder
}
@@ -771,6 +794,64 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
const Int effectiveHeight = (params.ImageHeight > 0) ? params.ImageHeight : height;
const SizeT inputRowStride = CalculateRowStride(effectiveWidth, pixelSize, params.Alignment);
// GL_DEPTH_COMPONENT client data may populate a packed depth-stencil internal
// format (the stencil half becomes zero); the generic channel converter cannot
// express the packed shadow words, so convert here.
const Bool packedDepthStencilInternal = targetInternalFormat == TextureInternalFormat::Depth24Stencil8 ||
targetInternalFormat == TextureInternalFormat::DepthStencil ||
targetInternalFormat == TextureInternalFormat::Depth32FStencil8;
if (!isBitmap && packedDepthStencilInternal && textureInputFormat == TextureInputFormat::DepthComponent &&
(inputDataType == TexturePixelDataType::Float || inputDataType == TexturePixelDataType::UnsignedInt ||
inputDataType == TexturePixelDataType::UnsignedShort)) {
const Bool floatShadow = targetInternalFormat == TextureInternalFormat::Depth32FStencil8;
const SizeT outPixelSize = floatShadow ? 8 : 4;
outSize = static_cast<SizeT>(width) * height * std::max(depth, 1) * outPixelSize;
Uint8* outputPixels = static_cast<Uint8*>(malloc(outSize));
if (!outputPixels) {
outSize = 0;
return nullptr;
}
const Uint8* srcBase = static_cast<const Uint8*>(inputPixels) +
static_cast<SizeT>(params.SkipImages) * static_cast<SizeT>(effectiveHeight) * inputRowStride +
static_cast<SizeT>(params.SkipRows) * inputRowStride +
static_cast<SizeT>(params.SkipPixels) * pixelSize;
Uint8* dst = outputPixels;
for (Int z = 0; z < std::max(depth, 1); ++z) {
for (Int y = 0; y < height; ++y) {
const Uint8* srcRow = srcBase +
static_cast<SizeT>(z) * static_cast<SizeT>(effectiveHeight) * inputRowStride +
static_cast<SizeT>(y) * inputRowStride;
for (Int x = 0; x < width; ++x) {
Float depthValue = 0.0f;
if (inputDataType == TexturePixelDataType::Float) {
Memcpy(&depthValue, srcRow + static_cast<SizeT>(x) * 4, sizeof(depthValue));
} else if (inputDataType == TexturePixelDataType::UnsignedInt) {
Uint32 raw = 0;
Memcpy(&raw, srcRow + static_cast<SizeT>(x) * 4, sizeof(raw));
depthValue = static_cast<Float>(static_cast<double>(raw) / 4294967295.0);
} else {
Uint16 raw = 0;
Memcpy(&raw, srcRow + static_cast<SizeT>(x) * 2, sizeof(raw));
depthValue = static_cast<Float>(raw) / 65535.0f;
}
if (floatShadow) {
const Uint32 stencilWord = 0;
Memcpy(dst, &depthValue, sizeof(depthValue));
Memcpy(dst + 4, &stencilWord, sizeof(stencilWord));
dst += 8;
} else {
const Uint32 depth24 = static_cast<Uint32>(
std::llround(static_cast<double>(std::clamp(depthValue, 0.0f, 1.0f)) * 16777215.0));
const Uint32 word = depth24 << 8;
Memcpy(dst, &word, sizeof(word));
dst += 4;
}
}
}
}
return outputPixels;
}
UnpackConversionSpec conversion{};
const Bool needConversion =
!isBitmap && GetUnpackConversionSpec(targetInternalFormat, textureInputFormat, inputDataType, conversion);
@@ -972,6 +1053,11 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
Memcpy(&v, p, sizeof(v));
return v;
}
case ShadowComponent::UNorm32: {
Uint32 v;
Memcpy(&v, p, sizeof(v));
return static_cast<Float>(static_cast<double>(v) / 4294967295.0);
}
default:
return 0.0f;
}