mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (DirectGLES): upload RGB565/RGB5_A1 shadow data as packed 16-bit types
- The 8-bit unorm shadow was uploaded as GL_UNSIGNED_BYTE, leaving the 8->5/6-bit requantization to the driver - That rounding direction is implementation-defined: Adreno rounds to nearest (lossless round trip), Mali floors - On Mali mid-range texels drifted one 5-bit step down, failing all 20 KHR-GL33.pixelstoragemodes.teximage3d rgb565/rgb5a1 cases (eps 1/32); layers with exact values (0.125/0.25/1.0-ish) passed, matching the observed 0,1,7-valid pattern - PreparePackedNormUpload repacks shadow rows to GL_UNSIGNED_SHORT_5_6_5 / 5_5_5_1 with round-to-nearest, which exactly recovers the original 5/6-bit values (the shadow expansion is injective), so the driver stores them verbatim - Idempotent across each region's level loop (glType is shared); RGBA4 exempt since its 8-bit expansion (v*17) is exact under either rounding - Wired at all four SyncMipmapsToBackend upload regions (append-mipmaps, immutable TexSubImage, mutable full, dirty-level update)
This commit is contained in:
@@ -1563,6 +1563,56 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return convertedData.data();
|
return convertedData.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RGB565/RGB5_A1 shadow data is stored as 8-bit unorm; uploading it as GL_UNSIGNED_BYTE
|
||||||
|
// leaves the 8-bit -> 5/6-bit requantization to the driver, whose rounding direction is
|
||||||
|
// implementation-defined: Adreno rounds to nearest (lossless round trip) but Mali floors,
|
||||||
|
// drifting mid-range texels one 5-bit step down and failing the KHR-GL3x
|
||||||
|
// pixelstoragemodes.teximage3d rgb565/rgb5a1 1/32-eps checks. Repack the shadow rows into
|
||||||
|
// the packed 16-bit client type with round-to-nearest instead - that recovers the original
|
||||||
|
// 5/6-bit values exactly (the shadow expansion round(v * 255 / max) is injective), so the
|
||||||
|
// driver stores them verbatim with no requantization left to its discretion. 4-bit formats
|
||||||
|
// (RGBA4) are exempt: their 8-bit expansion (v * 17) is exact under either rounding.
|
||||||
|
// Always retargets *inOutType for these formats (even for null data) so every upload of a
|
||||||
|
// level uses the same client type.
|
||||||
|
static const void* PreparePackedNormUpload(TextureInternalFormat format, const IntVec3& texelSize,
|
||||||
|
const void* data, SizeT byteSize, GLenum* inOutType,
|
||||||
|
Vector<Uint8>& packedData) {
|
||||||
|
if (format != TextureInternalFormat::RGB5 && format != TextureInternalFormat::RGB5A1) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const Bool hasAlpha = format == TextureInternalFormat::RGB5A1;
|
||||||
|
const GLenum packedType = hasAlpha ? GL_UNSIGNED_SHORT_5_5_5_1 : GL_UNSIGNED_SHORT_5_6_5;
|
||||||
|
// Idempotent across a region's level loop: glType is shared, so later levels arrive with
|
||||||
|
// the already-retargeted packed type and must still be converted.
|
||||||
|
if (*inOutType != GL_UNSIGNED_BYTE && *inOutType != packedType) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
*inOutType = packedType;
|
||||||
|
if (data == nullptr || byteSize == 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const SizeT srcPixelBytes = hasAlpha ? 4 : 3;
|
||||||
|
const SizeT texelCount = std::min(static_cast<SizeT>(std::max(texelSize.x(), 0)) *
|
||||||
|
static_cast<SizeT>(std::max(texelSize.y(), 0)) *
|
||||||
|
static_cast<SizeT>(std::max(texelSize.z(), 1)),
|
||||||
|
byteSize / srcPixelBytes);
|
||||||
|
packedData.resize(texelCount * sizeof(Uint16));
|
||||||
|
const Uint8* src = static_cast<const Uint8*>(data);
|
||||||
|
auto* dst = reinterpret_cast<Uint16*>(packedData.data());
|
||||||
|
for (SizeT i = 0; i < texelCount; ++i, src += srcPixelBytes) {
|
||||||
|
const Uint32 r = (static_cast<Uint32>(src[0]) * 31u + 127u) / 255u;
|
||||||
|
const Uint32 b = (static_cast<Uint32>(src[2]) * 31u + 127u) / 255u;
|
||||||
|
if (hasAlpha) {
|
||||||
|
const Uint32 g = (static_cast<Uint32>(src[1]) * 31u + 127u) / 255u;
|
||||||
|
dst[i] = static_cast<Uint16>((r << 11) | (g << 6) | (b << 1) | (src[3] >= 128 ? 1u : 0u));
|
||||||
|
} else {
|
||||||
|
const Uint32 g = (static_cast<Uint32>(src[1]) * 63u + 127u) / 255u;
|
||||||
|
dst[i] = static_cast<Uint16>((r << 11) | (g << 5) | b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packedData.data();
|
||||||
|
}
|
||||||
|
|
||||||
void BackendTextureObject::SyncMipmapsToBackend(
|
void BackendTextureObject::SyncMipmapsToBackend(
|
||||||
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
|
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
|
||||||
if (!stateTextureObject) {
|
if (!stateTextureObject) {
|
||||||
@@ -1706,6 +1756,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const void* uploadData = PrepareNormFloatFallbackUpload(
|
const void* uploadData = PrepareNormFloatFallbackUpload(
|
||||||
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
||||||
convertedUploadData);
|
convertedUploadData);
|
||||||
|
Vector<Uint8> packedUploadData;
|
||||||
|
uploadData = PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||||
|
uploadData, levelByteSize, &glType, packedUploadData);
|
||||||
|
|
||||||
DebugImpl::ErrorLopper::Clear();
|
DebugImpl::ErrorLopper::Clear();
|
||||||
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
@@ -1831,6 +1884,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const void* uploadData = PrepareNormFloatFallbackUpload(
|
const void* uploadData = PrepareNormFloatFallbackUpload(
|
||||||
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
||||||
convertedUploadData);
|
convertedUploadData);
|
||||||
|
Vector<Uint8> packedUploadData;
|
||||||
|
uploadData =
|
||||||
|
PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||||
|
uploadData, levelByteSize, &glType, packedUploadData);
|
||||||
|
|
||||||
DebugImpl::ErrorLopper::Clear();
|
DebugImpl::ErrorLopper::Clear();
|
||||||
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
@@ -1885,6 +1942,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const void* uploadData = PrepareNormFloatFallbackUpload(
|
const void* uploadData = PrepareNormFloatFallbackUpload(
|
||||||
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
textureMipmapObject->GetFormat(), levelTexelSize, pData, levelByteSize, glType,
|
||||||
convertedUploadData);
|
convertedUploadData);
|
||||||
|
Vector<Uint8> packedUploadData;
|
||||||
|
uploadData =
|
||||||
|
PreparePackedNormUpload(textureMipmapObject->GetFormat(), levelTexelSize,
|
||||||
|
uploadData, levelByteSize, &glType, packedUploadData);
|
||||||
MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, "
|
MGLOG_D("%s: target: %s: syncing mip %d: %dx%dx%d, byteSize = %d, pData = %p, "
|
||||||
"levelDirty = %s",
|
"levelDirty = %s",
|
||||||
__func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
|
__func__, MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
|
||||||
@@ -1990,6 +2051,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const void* uploadData = PrepareNormFloatFallbackUpload(
|
const void* uploadData = PrepareNormFloatFallbackUpload(
|
||||||
textureMipmapObject->GetFormat(), texelSize, mipData, byteSize, glType,
|
textureMipmapObject->GetFormat(), texelSize, mipData, byteSize, glType,
|
||||||
convertedUploadData);
|
convertedUploadData);
|
||||||
|
Vector<Uint8> packedUploadData;
|
||||||
|
uploadData = PreparePackedNormUpload(textureMipmapObject->GetFormat(), texelSize,
|
||||||
|
uploadData, byteSize, &glType, packedUploadData);
|
||||||
const IntVec3 uploadSize =
|
const IntVec3 uploadSize =
|
||||||
GetBackendUploadSize(stateTextureObject->GetTarget(), texelSize);
|
GetBackendUploadSize(stateTextureObject->GetTarget(), texelSize);
|
||||||
switch (MapToBackendTextureTarget(stateTextureObject->GetTarget())) {
|
switch (MapToBackendTextureTarget(stateTextureObject->GetTarget())) {
|
||||||
|
|||||||
Reference in New Issue
Block a user