mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
glCompressedTexImage2D rejected every internalformat with GL_INVALID_ENUM, so direct_state_access.textures_get_image threw at its first compressed call and reported InternalError with nothing in the log at all - the uncompressed half of the case had already passed. The compressed bytes are now kept verbatim, in a side-channel beside the texel shadow rather than in place of it. That placement is the load-bearing decision: both backends pair MapMipmapData with GetMipmapByteSize while sizing their copy regions from GetMipmapTexelSize, and DirectGLES additionally divides the byte size by the texel count to recover bytes-per-texel, so putting 16 bytes where a 4x4 RGBA8 extent says 64 would be an out-of-bounds read on both. The texel storage therefore stays uncompressed and correctly sized - the image samples as zeros, which is the same deviation the RGTC/BPTC/ETC2 arms of ConvertGLEnumToTextureInternalFormat already document - while glGetCompressedTexImage returns the image *as stored*, which GL 4.6 core 8.11 requires and which no re-encode could satisfy byte for byte. Nothing ever hands the compressed bytes to GLES or Vulkan, so the shadow is authoritative rather than potentially stale, which is why the readback never asks a backend. The accepted set is exactly the RGTC/BPTC/ETC2-EAC formats core GL requires, and it is deliberately the same set ConvertGLEnumToTextureInternalFormat can back with uncompressed storage, so the upload can never accept a format whose texel shadow it cannot allocate. imageSize is checked against the block arithmetic, which is also what keeps the copy in bounds. Three things the shape depends on. AllocateStorage clears the compressed tag, so a glTexImage2D or glTexStorage2D over the level un-compresses it - without that, textures_compressed_subimage would flip branches and start asking for data MobileGL cannot produce. GL_TEXTURE_COMPRESSED and GL_TEXTURE_COMPRESSED_IMAGE_SIZE are answered per level rather than per texture, because a compressed internalformat handed to glTexImage2D resolves to uncompressed storage and must keep reading as uncompressed. And GL_TEXTURE_INTERNAL_FORMAT now reports the compressed token for such a level, or it would claim GL_RGBA8 while GL_TEXTURE_COMPRESSED said true. Still rejected on purpose: glCompressedTexImage1D/3D and every glCompressedTexSubImage*, which caps the blast radius. Fixes textures_get_image on both backends (Espryt 370/371, Magma 369/371). A/B over a 1210-case compressed/texture-storage/texture-view/buffer-storage subset of KHR-GL45 is identical before and after on both backends but for get_texture_sub_image.errors_test, which stops throwing and fails on a value instead.
52 lines
3.0 KiB
C++
52 lines
3.0 KiB
C++
// MobileGL - MobileGL/MG_State/GLState/TextureState/TextureObject2DCube.h
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#pragma once
|
|
#include "TextureObject.h"
|
|
|
|
namespace MobileGL {
|
|
namespace MG_State {
|
|
namespace GLState {
|
|
class TextureObject2DCube : public TextureObjectMipmap {
|
|
public:
|
|
explicit TextureObject2DCube(Uint externalIndex);
|
|
|
|
const Vector<TextureUploadTarget>& GetUploadTargets() const override { return m_uploadTargets; }
|
|
|
|
Uint GetMipmapLevelCount() const override;
|
|
const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
|
const SizeT GetMipmapByteSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
|
void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) override;
|
|
void TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) override;
|
|
void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) override;
|
|
void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override;
|
|
void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override;
|
|
bool IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override;
|
|
void SetMipmapCompressedImage(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
|
GLenum internalFormat, const void* data, SizeT size) override;
|
|
GLenum GetMipmapCompressedFormat(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override;
|
|
SizeT GetMipmapCompressedByteSize(TextureUploadTarget uploadTarget, Uint mipmapLevel) const override;
|
|
const void* MapMipmapCompressedImage(TextureUploadTarget uploadTarget,
|
|
Uint mipmapLevel) const override;
|
|
|
|
IntVec3 GetBaseSize() const override;
|
|
Bool IsComplete() const override;
|
|
|
|
protected:
|
|
Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const override;
|
|
MipmapUploadTargetArray<6> m_textureStorage;
|
|
const Vector<TextureUploadTarget> m_uploadTargets{
|
|
TextureUploadTarget::CubeMapPositiveX, TextureUploadTarget::CubeMapNegativeX,
|
|
TextureUploadTarget::CubeMapPositiveY, TextureUploadTarget::CubeMapNegativeY,
|
|
TextureUploadTarget::CubeMapPositiveZ, TextureUploadTarget::CubeMapNegativeZ,
|
|
};
|
|
};
|
|
} // namespace GLState
|
|
} // namespace MG_State
|
|
} // namespace MobileGL
|