[Fix] (MG_State/GLState, MG_Impl/GLImpl): track immutable texture storage [skip ci]

This commit is contained in:
2026-07-02 22:49:54 +08:00
parent edec6e4e62
commit e83e6ed76e
3 changed files with 158 additions and 13 deletions
+123 -13
View File
@@ -99,6 +99,44 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_Util::ConvertGLEnumToTexturePixelDataType(realType));
}
Bool IsSizedTextureStorageInternalFormat(TextureInternalFormat textureInternalFormat) {
switch (textureInternalFormat) {
case TextureInternalFormat::Red:
case TextureInternalFormat::RG:
case TextureInternalFormat::RGB:
case TextureInternalFormat::RGBA:
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthStencil:
case TextureInternalFormat::Unknown:
return false;
default:
return true;
}
}
Bool ValidateTextureStorageInternalFormat(TextureInternalFormat textureInternalFormat, const char* caller) {
// TODO: Replace this sized-format filter with the full ARB_texture_storage legal-format table.
if (!IsSizedTextureStorageInternalFormat(textureInternalFormat)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"TexStorage requires a sized internal format."));
return false;
}
return TextureImpl::ValidateTextureInternalFormat(textureInternalFormat);
}
Bool ValidateTextureMutable(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
const char* caller) {
if (!textureObject || !textureObject->IsImmutable()) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"Immutable texture storage cannot be redefined."));
return false;
}
GLint GetTextureComponentType(TextureInternalFormat textureInternalFormat, GLint size, Bool depthComponent,
Bool stencilComponent) {
if (size <= 0) return GL_NONE;
@@ -529,6 +567,12 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_TEXTURE_MAX_LEVEL:
*params = static_cast<GLint>(textureObject->GetLevelRange().y());
break;
case GL_TEXTURE_IMMUTABLE_FORMAT:
*params = textureObject->IsImmutable() ? GL_TRUE : GL_FALSE;
break;
case GL_TEXTURE_IMMUTABLE_LEVELS:
*params = static_cast<GLint>(textureObject->GetImmutableLevels());
break;
case GL_TEXTURE_WRAP_S:
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS());
break;
@@ -720,6 +764,12 @@ namespace MobileGL::MG_Impl::GLImpl {
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
"Texture object here should always be an object with mipmap");
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
return;
}
auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level);
SizeT inputSize = 0;
@@ -1206,6 +1256,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// ===================== Error Checking ==============================
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
if (!ValidateTextureMutable(textureObject, __func__)) return;
// ======================= Processing ================================
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
@@ -1319,6 +1370,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// ===================== Error Checking ==============================
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
if (!ValidateTextureMutable(textureObject, __func__)) return;
// ======================= Processing ================================
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
@@ -1422,6 +1474,7 @@ namespace MobileGL::MG_Impl::GLImpl {
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
: bindingSlot.GetBoundObject();
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
if (!ValidateTextureMutable(textureObject, __func__)) return;
if (internalFormat == GL_ALPHA || format == GL_ALPHA) {
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
@@ -1620,6 +1673,16 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = static_cast<GLint>(textureObject->GetLevelRange().y());
}
break;
case GL_TEXTURE_IMMUTABLE_FORMAT:
if (params) {
*params = textureObject->IsImmutable() ? GL_TRUE : GL_FALSE;
}
break;
case GL_TEXTURE_IMMUTABLE_LEVELS:
if (params) {
*params = static_cast<GLint>(textureObject->GetImmutableLevels());
}
break;
case GL_TEXTURE_BORDER_COLOR:
if (params) {
const auto& borderColor = textureObject->GetBorderColor();
@@ -1748,6 +1811,16 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = static_cast<GLfloat>(textureObject->GetLevelRange().y());
}
break;
case GL_TEXTURE_IMMUTABLE_FORMAT:
if (params) {
*params = textureObject->IsImmutable() ? 1.0f : 0.0f;
}
break;
case GL_TEXTURE_IMMUTABLE_LEVELS:
if (params) {
*params = static_cast<GLfloat>(textureObject->GetImmutableLevels());
}
break;
case GL_TEXTURE_BORDER_COLOR:
if (params) {
const auto& borderColor = textureObject->GetBorderColor();
@@ -2165,9 +2238,14 @@ namespace MobileGL::MG_Impl::GLImpl {
// TODO: implement
}
void CopyTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
Bool CopyTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border) {
auto internalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
if (!ValidateTextureMutable(textureObject, __func__)) return false;
const auto& currentReadFBO =
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
if (!currentReadFBO) {
@@ -2175,7 +2253,7 @@ namespace MobileGL::MG_Impl::GLImpl {
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CopyTexImage2D_State",
"No framebuffer is currently bound to the GL_READ_FRAMEBUFFER target."));
return;
return false;
}
Bool isDepth = MG_Util::IsDepthFormatInternalFormat(internalFormat);
@@ -2194,7 +2272,7 @@ namespace MobileGL::MG_Impl::GLImpl {
ErrorCode::InvalidOperation, \
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CopyTexImage2D_State", \
"The attachment specified by the read buffer is incomplete.")); \
return; \
return false; \
}
if (isDepth) {
GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Depth);
@@ -2217,6 +2295,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
TexImage2D_State(target, level, (GLint)realInternalFormat, width, height, border, format, type, nullptr);
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).Bind(pixelUnpackBufferObject);
return true;
}
void CopyTexImage2D_Backend(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
@@ -2252,18 +2331,33 @@ namespace MobileGL::MG_Impl::GLImpl {
void CompressedTexImage3D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
if (!ValidateTextureMutable(textureObject, __func__)) return;
// TODO: implement
THROW_UNIMPL_EXCEPTION;
}
void CompressedTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
GLint border, GLsizei imageSize, const void* data) {
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
if (!ValidateTextureMutable(textureObject, __func__)) return;
// TODO: implement
THROW_UNIMPL_EXCEPTION;
}
void CompressedTexImage1D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
GLsizei imageSize, const void* data) {
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
if (!ValidateTextureMutable(textureObject, __func__)) return;
// TODO: implement
THROW_UNIMPL_EXCEPTION;
}
@@ -2567,15 +2661,14 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return;
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
TexturePixelDataType::UnsignedByte);
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
return;
}
if (!ValidateTextureMutable(textureObject, __func__)) return;
const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
@@ -2588,6 +2681,7 @@ namespace MobileGL::MG_Impl::GLImpl {
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, 1, 1}, byteSize});
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
}
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
}
void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
@@ -2602,15 +2696,21 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return;
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
TexturePixelDataType::UnsignedByte);
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
return;
}
if (!ValidateTextureMutable(textureObject, __func__)) return;
if (textureObject->GetTarget() == TextureTarget::TextureCubeMap && width != height) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"Cube map immutable storage must be square."));
return;
}
auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
@@ -2633,6 +2733,7 @@ namespace MobileGL::MG_Impl::GLImpl {
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
}
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
}
void TextureStorage3D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
@@ -2648,15 +2749,23 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return;
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
TexturePixelDataType::UnsignedByte);
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
return;
}
if (!ValidateTextureMutable(textureObject, __func__)) return;
if (textureObject->GetTarget() == TextureTarget::TextureCubeMapArray &&
(width != height || depth % 6 != 0)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__,
"Cube map array immutable storage must be square with depth multiple of 6."));
return;
}
const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
@@ -2673,6 +2782,7 @@ namespace MobileGL::MG_Impl::GLImpl {
{{levelWidth, levelHeight, levelDepth}, byteSize});
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
}
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
}
void TextureStorage2DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width,
@@ -3415,7 +3525,7 @@ namespace MobileGL::MG_Impl::GLImpl {
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border) {
CopyTexImage2D_State(target, level, internalformat, x, y, width, height, border);
if (!CopyTexImage2D_State(target, level, internalformat, x, y, width, height, border)) return;
CopyTexImage2D_Backend(target, level, internalformat, x, y, width, height, border);
}
@@ -162,19 +162,47 @@ namespace MobileGL {
}
void TextureObjectBase::SetBaseLevel(Uint baseLevel) {
if (IsImmutable() && m_immutableLevels > 0) {
baseLevel = std::min(baseLevel, m_immutableLevels - 1);
}
if (baseLevel == m_levelRange.x()) return;
m_levelRange.x() = baseLevel;
if (IsImmutable() && m_levelRange.y() < m_levelRange.x()) {
m_levelRange.y() = m_levelRange.x();
}
++m_textureParamsVersion;
}
void TextureObjectBase::SetMaxLevel(Uint maxLevel) {
if (IsImmutable() && m_immutableLevels > 0) {
maxLevel = std::min(std::max(maxLevel, m_levelRange.x()), m_immutableLevels - 1);
}
if (maxLevel == m_levelRange.y()) return;
m_levelRange.y() = maxLevel;
++m_textureParamsVersion;
}
Bool TextureObjectBase::IsImmutable() const {
return m_immutableLevels > 0;
}
Uint TextureObjectBase::GetImmutableLevels() const {
return m_immutableLevels;
}
void TextureObjectBase::SetImmutableLevels(Uint levels) {
if (m_immutableLevels == levels) return;
m_immutableLevels = levels;
if (m_immutableLevels > 0) {
m_levelRange.x() = std::min(m_levelRange.x(), m_immutableLevels - 1);
m_levelRange.y() = std::min(std::max(m_levelRange.y(), m_levelRange.x()), m_immutableLevels - 1);
}
++m_textureParamsVersion;
}
Uint16 TextureObjectBase::GetTextureParamsVersion() const {
return m_textureParamsVersion;
}
@@ -43,6 +43,9 @@ namespace MobileGL::MG_State::GLState {
virtual const UintVec2& GetLevelRange() const = 0;
virtual void SetBaseLevel(Uint baseLevel) = 0;
virtual void SetMaxLevel(Uint maxLevel) = 0;
virtual Bool IsImmutable() const = 0;
virtual Uint GetImmutableLevels() const = 0;
virtual void SetImmutableLevels(Uint levels) = 0;
virtual Uint16 GetTextureParamsVersion() const = 0;
virtual Int GetSamples() const = 0;
virtual void SetSamples(Int samples) = 0;
@@ -79,6 +82,9 @@ namespace MobileGL::MG_State::GLState {
const UintVec2& GetLevelRange() const override;
void SetBaseLevel(Uint baseLevel) override;
void SetMaxLevel(Uint maxLevel) override;
Bool IsImmutable() const override;
Uint GetImmutableLevels() const override;
void SetImmutableLevels(Uint levels) override;
Uint16 GetTextureParamsVersion() const override;
Int GetSamples() const override;
void SetSamples(Int samples) override;
@@ -100,6 +106,7 @@ namespace MobileGL::MG_State::GLState {
Vec4<TextureSwizzleParam> m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green,
TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha};
UintVec2 m_levelRange = {0, 1000};
Uint m_immutableLevels = 0;
Uint16 m_textureParamsVersion = 0;
Int m_samples = 0;
Bool m_fixedSampleLocations = true;