mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-09 11:03:23 +09:00
[Fix, Test] (MG_Impl/GLImpl, MG_Test/Texture, MG_Util/Converters): report proxy texture level sizes
- Record the size a proxy glTexImage* asks for and answer it from glGetTexLevelParameter, or zeros when it passes the texture size limits - Map GL_PROXY_TEXTURE_1D and GL_PROXY_TEXTURE_3D to their upload targets
This commit is contained in:
@@ -2197,7 +2197,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
|
||||
// Allocate in TextureObject
|
||||
if (isProxy) {
|
||||
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
||||
TextureImpl::pProxyTextureManager->RecordProxyLevel(textureUploadTarget, level, {width, height, depth});
|
||||
} else {
|
||||
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
||||
@@ -2345,7 +2345,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
|
||||
// Allocate in TextureObject
|
||||
if (isProxy) {
|
||||
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
||||
TextureImpl::pProxyTextureManager->RecordProxyLevel(textureUploadTarget, level, {width, height, 1});
|
||||
} else {
|
||||
MGLOG_D("%s: Allocating %d bytes at mip %d", __func__, internalBytes, level);
|
||||
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||
@@ -2460,7 +2460,9 @@ 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 (!isProxy) {
|
||||
if (isProxy) {
|
||||
TextureImpl::pProxyTextureManager->RecordProxyLevel(textureUploadTarget, level, {width, 1, 1});
|
||||
} else {
|
||||
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, 1, 1}, internalBytes});
|
||||
// After AllocateStorage, which clears the tag. No block-compressed format has a 1D
|
||||
@@ -2905,7 +2907,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
case GL_TEXTURE_HEIGHT:
|
||||
case GL_TEXTURE_DEPTH: {
|
||||
const SizeT axis = pname == GL_TEXTURE_WIDTH ? 0 : (pname == GL_TEXTURE_HEIGHT ? 1 : 2);
|
||||
if (storageType == TextureStorageType::Mipmap && mipmapObject != nullptr) {
|
||||
// A proxy level never took storage; its size lives with the proxy manager.
|
||||
const auto proxySize =
|
||||
isProxy ? TextureImpl::pProxyTextureManager->GetProxyLevelTexelSize(uploadTarget, level) : std::nullopt;
|
||||
if (proxySize) {
|
||||
value = (*proxySize)[axis];
|
||||
} else if (storageType == TextureStorageType::Mipmap && mipmapObject != nullptr) {
|
||||
value = mipmapObject->GetMipmapTexelSize(uploadTarget, level)[axis];
|
||||
} else if (storageType == TextureStorageType::Buffer) {
|
||||
// A buffer texture is one-dimensional.
|
||||
@@ -3988,10 +3995,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
||||
textureObject->SetInternalFormat(textureInternalFormat);
|
||||
|
||||
// A proxy records the format and nothing else - it must never take storage, and it must never
|
||||
// be tagged compressed, or GL_TEXTURE_COMPRESSED_IMAGE_SIZE on a proxy would stop being
|
||||
// INVALID_OPERATION.
|
||||
if (isProxy) return;
|
||||
// A proxy records the format and the level size and nothing else - it must never take
|
||||
// storage, and it must never be tagged compressed, or GL_TEXTURE_COMPRESSED_IMAGE_SIZE on a
|
||||
// proxy would stop being INVALID_OPERATION.
|
||||
if (isProxy) {
|
||||
TextureImpl::pProxyTextureManager->RecordProxyLevel(textureUploadTarget, level, {width, height, depth});
|
||||
return;
|
||||
}
|
||||
|
||||
const SizeT internalBpp =
|
||||
MG_Util::GetInternalBytesPerPixel(textureInternalFormat, TexturePixelDataType::UnsignedByte);
|
||||
@@ -4072,10 +4082,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
||||
textureObject->SetInternalFormat(textureInternalFormat);
|
||||
|
||||
// A proxy records the format and nothing else - it must never take storage, and it must never
|
||||
// be tagged compressed, or GL_TEXTURE_COMPRESSED_IMAGE_SIZE on a proxy would stop being
|
||||
// INVALID_OPERATION.
|
||||
if (isProxy) return;
|
||||
// A proxy records the format and the level size and nothing else - it must never take
|
||||
// storage, and it must never be tagged compressed, or GL_TEXTURE_COMPRESSED_IMAGE_SIZE on a
|
||||
// proxy would stop being INVALID_OPERATION.
|
||||
if (isProxy) {
|
||||
TextureImpl::pProxyTextureManager->RecordProxyLevel(textureUploadTarget, level, {width, height, 1});
|
||||
return;
|
||||
}
|
||||
|
||||
const SizeT internalBpp =
|
||||
MG_Util::GetInternalBytesPerPixel(textureInternalFormat, TexturePixelDataType::UnsignedByte);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// End of Source File Header
|
||||
|
||||
#include "ProxyTexture.h"
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_State/GLState/TextureState/TextureObject1D.h>
|
||||
#include <MG_State/GLState/TextureState/TextureObject2D.h>
|
||||
#include <MG_State/GLState/TextureState/TextureObject2DCube.h>
|
||||
@@ -87,4 +88,51 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
||||
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject = nullptr;
|
||||
return nullTextureObject;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Whether the implementation can hold a level of this size on this proxy target. Every
|
||||
// dimension of a mipmapped image shrinks with the level; array layers do not.
|
||||
Bool ProxyLevelFits(TextureUploadTarget target, GLint level, IntVec3 size) {
|
||||
MG_Backend::DynamicBackendParameters limits{};
|
||||
if (MG_Backend::pActiveBackendObject) limits = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
||||
const auto levelLimit = [level](Int limit) { return level < 31 ? std::max(limit >> level, 1) : 1; };
|
||||
const Int planar = levelLimit(limits.MaxTextureSize);
|
||||
const Int cube = levelLimit(limits.MaxCubeMapTextureSize);
|
||||
const Int volume = levelLimit(limits.Max3DTextureSize);
|
||||
const Int layers = limits.MaxArrayTextureLayers;
|
||||
switch (target) {
|
||||
case TextureUploadTarget::ProxyTexture1D:
|
||||
return size.x() <= planar;
|
||||
case TextureUploadTarget::ProxyTexture1DArray:
|
||||
return size.x() <= planar && size.y() <= layers;
|
||||
case TextureUploadTarget::ProxyTexture2DArray:
|
||||
return size.x() <= planar && size.y() <= planar && size.z() <= layers;
|
||||
case TextureUploadTarget::ProxyTexture3D:
|
||||
return size.x() <= volume && size.y() <= volume && size.z() <= volume;
|
||||
case TextureUploadTarget::ProxyCubeMap:
|
||||
return size.x() <= cube && size.y() <= cube;
|
||||
case TextureUploadTarget::ProxyCubeMapArray:
|
||||
return size.x() <= cube && size.y() <= cube && size.z() <= layers;
|
||||
case TextureUploadTarget::ProxyTextureRectangle:
|
||||
return level == 0 && size.x() <= limits.MaxTextureSize && size.y() <= limits.MaxTextureSize;
|
||||
default:
|
||||
return size.x() <= planar && size.y() <= planar;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ProxyTextureManager::RecordProxyLevel(TextureUploadTarget target, GLint level, IntVec3 size) {
|
||||
if (level < 0) return;
|
||||
auto& levels = m_proxyLevelSizes[target];
|
||||
if (levels.size() <= static_cast<SizeT>(level)) levels.resize(static_cast<SizeT>(level) + 1);
|
||||
levels[level] = ProxyLevelFits(target, level, size) ? size : IntVec3{0, 0, 0};
|
||||
}
|
||||
|
||||
Optional<IntVec3> ProxyTextureManager::GetProxyLevelTexelSize(TextureUploadTarget target, GLint level) const {
|
||||
auto it = m_proxyLevelSizes.find(target);
|
||||
if (it == m_proxyLevelSizes.end() || level < 0 || it->second.size() <= static_cast<SizeT>(level)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second[level];
|
||||
}
|
||||
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
|
||||
|
||||
@@ -19,8 +19,15 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
||||
TextureUploadTarget target);
|
||||
const SharedPtr<MG_State::GLState::ITextureObject>& GetProxyTextureObject(TextureUploadTarget target);
|
||||
|
||||
// The image a proxy glTexImage* or glCompressedTexImage* describes, kept here because a proxy
|
||||
// never takes storage. A request past the implementation limits records all zeros (GL 4.6
|
||||
// core 8.22), which is how glGetTexLevelParameter tells the application it cannot be had.
|
||||
void RecordProxyLevel(TextureUploadTarget target, GLint level, IntVec3 size);
|
||||
Optional<IntVec3> GetProxyLevelTexelSize(TextureUploadTarget target, GLint level) const;
|
||||
|
||||
private:
|
||||
UnorderedMap<TextureUploadTarget, SharedPtr<MG_State::GLState::ITextureObject>> m_proxyTexturesMap;
|
||||
UnorderedMap<TextureUploadTarget, Vector<Optional<IntVec3>>> m_proxyLevelSizes;
|
||||
};
|
||||
|
||||
extern UniquePtr<ProxyTextureManager>& pProxyTextureManager;
|
||||
|
||||
@@ -1204,18 +1204,58 @@ TEST_F(TextureTest, TextureParameterRejectsProxyTargetsThatGetTexLevelParameterS
|
||||
ExpectSingleGlError(GL_INVALID_ENUM);
|
||||
|
||||
// The level query keeps its proxy support - that is the entire point of a proxy texture, and the
|
||||
// predicate deliberately does not gate it. Only the ERROR is asserted, not the width: MobileGL
|
||||
// does not currently report a proxy level's dimensions back (it answers 0), which is a separate
|
||||
// pre-existing gap in GetTexLevelParameter and not something this predicate decides. What
|
||||
// matters here is that the query is not turned into GL_INVALID_ENUM alongside the setters.
|
||||
// predicate deliberately does not gate it: the query answers the size the proxy was given
|
||||
// instead of turning into GL_INVALID_ENUM alongside the setters.
|
||||
GLint proxyWidth = -1;
|
||||
MG_Impl::GLImpl::GetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &proxyWidth);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR)
|
||||
<< "the proxy target must still be accepted by GetTexLevelParameter";
|
||||
EXPECT_EQ(proxyWidth, 4) << "a proxy level reports the width it was given";
|
||||
|
||||
DrainPendingGlErrors();
|
||||
}
|
||||
|
||||
// GL 4.6 core 8.22: a proxy level reports the size it was given while the implementation can hold
|
||||
// it, and all zeros - with no error - once a dimension passes the limit at that level. Applications
|
||||
// probe GL_MAX_TEXTURE_SIZE this way, halving from the largest size until the width comes back.
|
||||
TEST_F(TextureTest, ProxyTextureLevelReportsRequestedSizeOrZerosPastTheLimit) {
|
||||
GLint maxTextureSize = 0;
|
||||
MG_Impl::GLImpl::GetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
ASSERT_GT(maxTextureSize, 1);
|
||||
const auto levelSize = [](GLenum target, GLint level, GLenum pname) {
|
||||
GLint value = -1;
|
||||
MG_Impl::GLImpl::GetTexLevelParameteriv(target, level, pname, &value);
|
||||
return value;
|
||||
};
|
||||
|
||||
MG_Impl::GLImpl::TexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, maxTextureSize, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH), maxTextureSize);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT), 16);
|
||||
|
||||
MG_Impl::GLImpl::TexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, maxTextureSize * 2, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH), 0) << "past GL_MAX_TEXTURE_SIZE";
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT), 0) << "every dimension is cleared";
|
||||
|
||||
// The limit halves with each level.
|
||||
MG_Impl::GLImpl::TexImage2D(GL_PROXY_TEXTURE_2D, 1, GL_RGBA8, maxTextureSize, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 1, GL_TEXTURE_WIDTH), 0);
|
||||
MG_Impl::GLImpl::TexImage2D(GL_PROXY_TEXTURE_2D, 1, GL_RGBA8, maxTextureSize / 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_2D, 1, GL_TEXTURE_WIDTH), maxTextureSize / 2);
|
||||
|
||||
MG_Impl::GLImpl::TexImage1D(GL_PROXY_TEXTURE_1D, 0, GL_RGBA8, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH), 64);
|
||||
MG_Impl::GLImpl::TexImage3D(GL_PROXY_TEXTURE_3D, 0, GL_RGBA8, 8, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH), 8);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT), 4);
|
||||
EXPECT_EQ(levelSize(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH), 2);
|
||||
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR) << "an unsupported proxy size is not an error";
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, GenThenBindCreatesObjectForUnsizedPackedBgraSubImageUpload) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
|
||||
@@ -410,8 +410,12 @@ namespace MobileGL {
|
||||
return TextureUploadTarget::Texture2D;
|
||||
case GL_TEXTURE_3D:
|
||||
return TextureUploadTarget::Texture3D;
|
||||
case GL_PROXY_TEXTURE_1D:
|
||||
return TextureUploadTarget::ProxyTexture1D;
|
||||
case GL_PROXY_TEXTURE_2D:
|
||||
return TextureUploadTarget::ProxyTexture2D;
|
||||
case GL_PROXY_TEXTURE_3D:
|
||||
return TextureUploadTarget::ProxyTexture3D;
|
||||
case GL_TEXTURE_1D_ARRAY:
|
||||
return TextureUploadTarget::Texture1DArray;
|
||||
case GL_PROXY_TEXTURE_1D_ARRAY:
|
||||
|
||||
@@ -317,8 +317,12 @@ namespace MobileGL {
|
||||
return GL_TEXTURE_1D;
|
||||
case TextureUploadTarget::Texture3D:
|
||||
return GL_TEXTURE_3D;
|
||||
case TextureUploadTarget::ProxyTexture1D:
|
||||
return GL_PROXY_TEXTURE_1D;
|
||||
case TextureUploadTarget::ProxyTexture2D:
|
||||
return GL_PROXY_TEXTURE_2D;
|
||||
case TextureUploadTarget::ProxyTexture3D:
|
||||
return GL_PROXY_TEXTURE_3D;
|
||||
case TextureUploadTarget::Texture1DArray:
|
||||
return GL_TEXTURE_1D_ARRAY;
|
||||
case TextureUploadTarget::ProxyTexture1DArray:
|
||||
|
||||
Reference in New Issue
Block a user