mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
Compare commits
5
Commits
e724e88eec
...
86b02b59ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86b02b59ed | ||
|
|
cd0228d0c7 | ||
|
|
b1f4ef030a | ||
|
|
27ed00a9e5 | ||
|
|
1e69fe9669 |
@@ -2216,11 +2216,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextureImpl::IsMultisampleTextureTarget(targetInternal)) {
|
// Multisample targets reject the *sampler* parameters (LOD range, border color) but
|
||||||
|
// GL_TEXTURE_SWIZZLE_* is texture state, not sampler state, and ES accepts it on them.
|
||||||
|
// Bailing out entirely used to drop every swizzle write on the floor, which is what the
|
||||||
|
// frontend already assumes is legal (see GL_Texture.cpp's MS-invalid pname list, which
|
||||||
|
// deliberately omits the swizzle enums). Note the caches for the skipped parameters are
|
||||||
|
// still refreshed so they never look stale, but m_cacheSwizzleParams must NOT be, or the
|
||||||
|
// change detection below would swallow the very writes we came here to emit.
|
||||||
|
const Bool isMultisampleTarget = TextureImpl::IsMultisampleTextureTarget(targetInternal);
|
||||||
|
if (isMultisampleTarget) {
|
||||||
m_cacheLodRange = stateTextureObject->GetLevelRange();
|
m_cacheLodRange = stateTextureObject->GetLevelRange();
|
||||||
m_cacheSwizzleParams = stateTextureObject->GetAllSwizzleParams();
|
|
||||||
m_cacheBorderColor = stateTextureObject->GetBorderColor();
|
m_cacheBorderColor = stateTextureObject->GetBorderColor();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bind(target);
|
Bind(target);
|
||||||
@@ -2233,14 +2239,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
const auto& levelRange = stateTextureObject->GetLevelRange();
|
const auto& levelRange = stateTextureObject->GetLevelRange();
|
||||||
|
|
||||||
if (m_cacheLodRange.x() != levelRange.x()) {
|
if (!isMultisampleTarget && m_cacheLodRange.x() != levelRange.x()) {
|
||||||
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast<GLint>(levelRange.x()));
|
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast<GLint>(levelRange.x()));
|
||||||
m_cacheLodRange.x() = levelRange.x();
|
m_cacheLodRange.x() = levelRange.x();
|
||||||
}
|
}
|
||||||
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
|
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
|
||||||
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||||
});
|
});
|
||||||
if (m_cacheLodRange.y() != levelRange.y()) {
|
if (!isMultisampleTarget && m_cacheLodRange.y() != levelRange.y()) {
|
||||||
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(levelRange.y()));
|
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(levelRange.y()));
|
||||||
m_cacheLodRange.y() = levelRange.y();
|
m_cacheLodRange.y() = levelRange.y();
|
||||||
}
|
}
|
||||||
@@ -2266,7 +2272,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cacheBorderColor != stateTextureObject->GetBorderColor()) {
|
if (!isMultisampleTarget && m_cacheBorderColor != stateTextureObject->GetBorderColor()) {
|
||||||
const auto& borderColor = stateTextureObject->GetBorderColor();
|
const auto& borderColor = stateTextureObject->GetBorderColor();
|
||||||
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
|
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
|
||||||
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
|
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
|
||||||
|
|||||||
@@ -295,7 +295,13 @@ DECLARE_GL_FUNCTION_HEAD(void, VertexAttribDivisor, GLuint index, GLuint divisor
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTransformFeedback, GLenum target, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTransformFeedback, target, id)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTransformFeedback, GLenum target, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTransformFeedback, target, id)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteTransformFeedbacks, GLsizei n, const GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteTransformFeedbacks, n, ids)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteTransformFeedbacks, GLsizei n, const GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteTransformFeedbacks, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenTransformFeedbacks, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenTransformFeedbacks, n, ids)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenTransformFeedbacks, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenTransformFeedbacks, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsTransformFeedback, GLuint id) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsTransformFeedback, id)
|
// Transform feedback objects are not implemented, so no name is ever a live object. The shared
|
||||||
|
// stub returns (type)1, telling a probing caller that every id it invents already exists; GL_FALSE
|
||||||
|
// is both truthful and what the spec requires for a name that was never generated.
|
||||||
|
MOBILEGL_GL_API GLboolean glIsTransformFeedback(GLuint id) {
|
||||||
|
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
|
||||||
|
return GL_FALSE;
|
||||||
|
}
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, PauseTransformFeedback) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PauseTransformFeedback)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, PauseTransformFeedback) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PauseTransformFeedback)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResumeTransformFeedback) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResumeTransformFeedback)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResumeTransformFeedback) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResumeTransformFeedback)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary)
|
||||||
@@ -2583,7 +2589,10 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackStreamAttribsNV, GLsizei co
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTransformFeedbackNV, GLenum target, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTransformFeedbackNV, target, id)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTransformFeedbackNV, GLenum target, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTransformFeedbackNV, target, id)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteTransformFeedbacksNV, GLsizei n, const GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteTransformFeedbacksNV, n, ids)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteTransformFeedbacksNV, GLsizei n, const GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteTransformFeedbacksNV, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenTransformFeedbacksNV, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenTransformFeedbacksNV, n, ids)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenTransformFeedbacksNV, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenTransformFeedbacksNV, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsTransformFeedbackNV, GLuint id) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsTransformFeedbackNV, id)
|
MOBILEGL_GL_API GLboolean glIsTransformFeedbackNV(GLuint id) {
|
||||||
|
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
|
||||||
|
return GL_FALSE;
|
||||||
|
}
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, PauseTransformFeedbackNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PauseTransformFeedbackNV, )
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, PauseTransformFeedbackNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PauseTransformFeedbackNV, )
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResumeTransformFeedbackNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResumeTransformFeedbackNV, )
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResumeTransformFeedbackNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResumeTransformFeedbackNV, )
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DrawTransformFeedbackNV, GLenum mode, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DrawTransformFeedbackNV, mode, id)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, DrawTransformFeedbackNV, GLenum mode, GLuint id) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DrawTransformFeedbackNV, mode, id)
|
||||||
|
|||||||
@@ -350,6 +350,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize});
|
texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize});
|
||||||
texture.MarkStorageDirty(uploadTarget, level, false);
|
texture.MarkStorageDirty(uploadTarget, level, false);
|
||||||
}
|
}
|
||||||
|
// glGenerateMipmap defines exactly levels 0..requiredLevelCount-1. AllocateStorage only
|
||||||
|
// grows, so a previously longer chain (a bigger base image before respecification) would
|
||||||
|
// otherwise keep a tail of stale levels here and read as incomplete.
|
||||||
|
texture.TruncateMipmapLevels(uploadTarget, requiredLevelCount);
|
||||||
// Mip generation grows/regenerates the level set on the GPU without marking any CPU
|
// Mip generation grows/regenerates the level set on the GPU without marking any CPU
|
||||||
// level dirty (MarkStorageDirty(...,false) above). Bump the content version so the
|
// level dirty (MarkStorageDirty(...,false) above). Bump the content version so the
|
||||||
// backend re-syncs: a cached sampled VkImageView built for the pre-generate level
|
// backend re-syncs: a cached sampled VkImageView built for the pre-generate level
|
||||||
@@ -429,8 +433,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
const Int maxSamples = GetMaxSupportedTextureSamples(textureInternalFormat);
|
const Int maxSamples = GetMaxSupportedTextureSamples(textureInternalFormat);
|
||||||
if (samples > maxSamples) {
|
if (samples > maxSamples) {
|
||||||
|
// GL specifies INVALID_OPERATION - not INVALID_VALUE - when the sample count
|
||||||
|
// exceeds what the format supports, and the native Adreno driver agrees.
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidValue,
|
ErrorCode::InvalidOperation,
|
||||||
MakeUnique<GenericErrorInfo>(
|
MakeUnique<GenericErrorInfo>(
|
||||||
"MG_Impl/GLImpl", caller,
|
"MG_Impl/GLImpl", caller,
|
||||||
std::format("Sample count {} exceeds the supported maximum {} for this texture format.",
|
std::format("Sample count {} exceeds the supported maximum {} for this texture format.",
|
||||||
@@ -454,8 +460,56 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
textureObject->SetSamples(samples);
|
textureObject->SetSamples(samples);
|
||||||
textureObject->SetFixedSampleLocations(fixedsamplelocations == GL_TRUE);
|
textureObject->SetFixedSampleLocations(fixedsamplelocations == GL_TRUE);
|
||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, 0, {{width, height, depth}, 0});
|
textureMipmapObject->AllocateStorage(textureUploadTarget, 0, {{width, height, depth}, 0});
|
||||||
|
// Multisample textures are single-level by definition, so a name that previously held a
|
||||||
|
// mip chain must not keep its tail now that AllocateStorage only grows.
|
||||||
|
textureMipmapObject->TruncateMipmapLevels(textureUploadTarget, 1);
|
||||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, 0, false);
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redefining level 0 of a texture that already had a base image drops the rest of the chain,
|
||||||
|
// which is exactly what AllocateLevel used to do implicitly for every level. Keeping that
|
||||||
|
// behaviour for level 0 - and only for level 0 - is what makes the grow-only change safe:
|
||||||
|
// any level-0 respecification leaves the chain in precisely the state it would have had
|
||||||
|
// before, while an upload to level N no longer destroys the levels beneath it.
|
||||||
|
//
|
||||||
|
// Why it has to be *every* level-0 respecification and not just a size change: Minecraft's
|
||||||
|
// Mipmap Levels setting rebuilds the block atlas at the SAME dimensions with a different
|
||||||
|
// level count. A size-only test would leave the old tail in place, and because Mojang
|
||||||
|
// terminates its chains with a 0x0 level the result is the zero-then-nonzero pattern that
|
||||||
|
// IsComplete() rejects (TextureObject.cpp) - whereupon DirectGLES skips syncing the texture
|
||||||
|
// entirely (Managers.cpp) and the atlas samples black.
|
||||||
|
//
|
||||||
|
// The "already has a base image" test is what lets the fix work at all: a level that was
|
||||||
|
// never written reads back as {0,0,0}, so building a chain top-down - upload level N first,
|
||||||
|
// then level 0 - must not discard the levels just uploaded. That ordering is what
|
||||||
|
// KHR-GL33.texture_repeat_mode does.
|
||||||
|
// Scoped to the respecified upload target only, which is what AllocateLevel already did.
|
||||||
|
// Cube maps keep six independent chains while reporting a single level count (face +X), so
|
||||||
|
// respecifying a face other than +X can leave the count longer than that face - but that
|
||||||
|
// asymmetry predates this change and widening the truncation to all six faces would destroy
|
||||||
|
// mip data for faces the application never touched. Left alone deliberately.
|
||||||
|
void DiscardMipmapChainOnBaseRespecification(MG_State::GLState::TextureObjectMipmap* texture,
|
||||||
|
TextureUploadTarget uploadTarget, Uint level) {
|
||||||
|
if (level != 0) return;
|
||||||
|
|
||||||
|
const IntVec3 existingBaseSize = texture->GetMipmapTexelSize(uploadTarget, 0);
|
||||||
|
const Bool hasExistingBaseImage =
|
||||||
|
existingBaseSize.x() > 0 && existingBaseSize.y() > 0 && existingBaseSize.z() > 0;
|
||||||
|
if (!hasExistingBaseImage) return;
|
||||||
|
|
||||||
|
texture->TruncateMipmapLevels(uploadTarget, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compressed texture upload is not implemented yet. GL_NUM_COMPRESSED_TEXTURE_FORMATS
|
||||||
|
// reports 0, so every compressed internalformat is by definition unsupported and
|
||||||
|
// GL_INVALID_ENUM is the specified error - unlike THROW_UNIMPL_EXCEPTION, which unwinds
|
||||||
|
// a C++ exception through the C GL ABI and takes the process down.
|
||||||
|
void RecordUnsupportedCompressedFormat(const char* caller) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
||||||
|
"Compressed texture formats are not supported."));
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByName(GLuint texture, const char* caller) {
|
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByName(GLuint texture, const char* caller) {
|
||||||
@@ -1727,6 +1781,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
if (isProxy) {
|
if (isProxy) {
|
||||||
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
||||||
} else {
|
} else {
|
||||||
|
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1854,6 +1909,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
||||||
} else {
|
} else {
|
||||||
MGLOG_D("%s: Allocating %d bytes at mip %d", __func__, internalBytes, level);
|
MGLOG_D("%s: Allocating %d bytes at mip %d", __func__, internalBytes, level);
|
||||||
|
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level,
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level,
|
||||||
{{width, height, 1}, internalBytes});
|
{{width, height, 1}, internalBytes});
|
||||||
}
|
}
|
||||||
@@ -1942,6 +1998,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
"Texture object here should always be an object with mipmap");
|
"Texture object here should always be an object with mipmap");
|
||||||
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
if (!isProxy) {
|
if (!isProxy) {
|
||||||
|
DiscardMipmapChainOnBaseRespecification(textureMipmapObject, textureUploadTarget, level);
|
||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, 1, 1}, internalBytes});
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, 1, 1}, internalBytes});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2594,7 +2651,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetCompressedTexImage_State(GLenum target, GLint level, void* img) {
|
void GetCompressedTexImage_State(GLenum target, GLint level, void* img) {
|
||||||
// TODO: implement
|
// TODO: implement compressed readback. Reporting success while writing nothing hands
|
||||||
|
// the caller stale memory with GL_NO_ERROR; no texture can be compressed yet, and GL
|
||||||
|
// specifies GL_INVALID_OPERATION when the bound level is not compressed.
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"Texture level is not stored in a compressed format."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenTextures_State(GLsizei n, GLuint* textures) {
|
void GenTextures_State(GLsizei n, GLuint* textures) {
|
||||||
@@ -2781,20 +2844,20 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void CompressedTexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
void CompressedTexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
||||||
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
||||||
const void* data) {
|
const void* data) {
|
||||||
// TODO: implement
|
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||||
THROW_UNIMPL_EXCEPTION;
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedTexSubImage2D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
void CompressedTexSubImage2D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
||||||
GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
|
GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
|
||||||
// TODO: implement
|
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||||
THROW_UNIMPL_EXCEPTION;
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedTexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
void CompressedTexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
||||||
GLsizei imageSize, const void* data) {
|
GLsizei imageSize, const void* data) {
|
||||||
// TODO: implement
|
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||||
THROW_UNIMPL_EXCEPTION;
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedTexImage3D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
void CompressedTexImage3D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
||||||
@@ -2804,8 +2867,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||||
THROW_UNIMPL_EXCEPTION;
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
void CompressedTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
||||||
@@ -2815,8 +2878,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement compressed upload. Until then report the spec error for an
|
||||||
THROW_UNIMPL_EXCEPTION;
|
// unsupported compressed format rather than throwing - a C++ exception unwinding
|
||||||
|
// through the C GL ABI is a hard crash for the caller, while GL_INVALID_ENUM is
|
||||||
|
// exactly what GL_NUM_COMPRESSED_TEXTURE_FORMATS == 0 promises.
|
||||||
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressedTexImage1D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
|
void CompressedTexImage1D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
|
||||||
@@ -2826,8 +2892,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
||||||
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement compressed upload - see CompressedTexImage2D_State.
|
||||||
THROW_UNIMPL_EXCEPTION;
|
RecordUnsupportedCompressedFormat(__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindTexture_State(GLenum target, GLuint texture) {
|
void BindTexture_State(GLenum target, GLuint texture) {
|
||||||
@@ -3173,6 +3239,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, 1, 1}, byteSize});
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, 1, 1}, byteSize});
|
||||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
||||||
}
|
}
|
||||||
|
// Immutable storage defines exactly `levels` levels; AllocateStorage only grows, so a
|
||||||
|
// longer pre-existing chain has to be dropped explicitly.
|
||||||
|
textureMipmapObject->TruncateMipmapLevels(textureUploadTarget, static_cast<Uint>(levels));
|
||||||
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3225,6 +3294,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
|
||||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
||||||
}
|
}
|
||||||
|
// See TextureStorage1D.
|
||||||
|
textureMipmapObject->TruncateMipmapLevels(textureUploadTarget, static_cast<Uint>(levels));
|
||||||
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3277,6 +3348,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
{{levelWidth, levelHeight, levelDepth}, byteSize});
|
{{levelWidth, levelHeight, levelDepth}, byteSize});
|
||||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
||||||
}
|
}
|
||||||
|
// See TextureStorage1D.
|
||||||
|
textureMipmapObject->TruncateMipmapLevels(textureUploadTarget, static_cast<Uint>(levels));
|
||||||
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,17 +16,32 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MipmapStorage::AllocateLevel(Uint level, MipmapInput input) {
|
void MipmapStorage::AllocateLevel(Uint level, MipmapInput input) {
|
||||||
m_data.reserve(std::bit_ceil(level + 1));
|
// Grow only. GL respecifies exactly the level it is handed, so allocating level 0
|
||||||
m_data.resize(level + 1);
|
// must not disturb the levels above it - but resize() shrinks as readily as it
|
||||||
m_texelSizes.reserve(std::bit_ceil(level + 1));
|
// grows, so this used to truncate the whole chain to a single level. Callers that
|
||||||
m_texelSizes.resize(level + 1);
|
// genuinely redefine the complete level set say so with TruncateToLevelCount.
|
||||||
m_texelSizes[level] = input.texelSize;
|
const SizeT requiredLevelCount = static_cast<SizeT>(level) + 1;
|
||||||
m_isDirty.resize(level + 1, false);
|
if (m_data.size() < requiredLevelCount) {
|
||||||
|
m_data.reserve(std::bit_ceil(requiredLevelCount));
|
||||||
|
m_data.resize(requiredLevelCount);
|
||||||
|
m_texelSizes.reserve(std::bit_ceil(requiredLevelCount));
|
||||||
|
m_texelSizes.resize(requiredLevelCount);
|
||||||
|
m_isDirty.resize(requiredLevelCount, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_texelSizes[level] = input.texelSize;
|
||||||
auto& data = m_data[level];
|
auto& data = m_data[level];
|
||||||
data.resize(input.byteSize, 0);
|
data.resize(input.byteSize, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MipmapStorage::TruncateToLevelCount(SizeT levelCount) {
|
||||||
|
if (levelCount >= m_data.size()) return;
|
||||||
|
|
||||||
|
m_data.resize(levelCount);
|
||||||
|
m_texelSizes.resize(levelCount);
|
||||||
|
m_isDirty.resize(levelCount);
|
||||||
|
}
|
||||||
|
|
||||||
void MipmapStorage::UpdateSubData(Uint level, DataPtr input) {
|
void MipmapStorage::UpdateSubData(Uint level, DataPtr input) {
|
||||||
auto& targetData = m_data;
|
auto& targetData = m_data;
|
||||||
MOBILEGL_ASSERT(level < targetData.size(), "UpdateSubData: level out of range");
|
MOBILEGL_ASSERT(level < targetData.size(), "UpdateSubData: level out of range");
|
||||||
@@ -55,6 +70,7 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SizeT MipmapStorage::GetByteSize(Uint level) const {
|
SizeT MipmapStorage::GetByteSize(Uint level) const {
|
||||||
|
if (level >= m_data.size()) return 0;
|
||||||
return m_data[level].size();
|
return m_data[level].size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ namespace MobileGL {
|
|||||||
public:
|
public:
|
||||||
SizeT GetLevelCount() const;
|
SizeT GetLevelCount() const;
|
||||||
void AllocateLevel(Uint level, MipmapInput input);
|
void AllocateLevel(Uint level, MipmapInput input);
|
||||||
|
// Discard every level at or above levelCount. AllocateLevel never shrinks, so this
|
||||||
|
// is the only way a chain gets shorter - use it where the caller defines the whole
|
||||||
|
// level set (glTexStorage*, mip regeneration, atlas respecification).
|
||||||
|
void TruncateToLevelCount(SizeT levelCount);
|
||||||
void UpdateSubData(Uint level, DataPtr input);
|
void UpdateSubData(Uint level, DataPtr input);
|
||||||
void* MapData(Uint level);
|
void* MapData(Uint level);
|
||||||
IntVec3 GetTexelSize(Uint level) const;
|
IntVec3 GetTexelSize(Uint level) const;
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ namespace MobileGL {
|
|||||||
m_storage[targetIndex].AllocateLevel(level, input);
|
m_storage[targetIndex].AllocateLevel(level, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-target, like AllocateLevel: cube-map faces are respecified independently, so
|
||||||
|
// truncating one face must not disturb the others.
|
||||||
|
void TruncateToLevelCount(Uint targetIndex, SizeT levelCount) {
|
||||||
|
MOBILEGL_ASSERT(targetIndex < TargetCount, "TruncateToLevelCount: target invalid");
|
||||||
|
|
||||||
|
m_storage[targetIndex].TruncateToLevelCount(levelCount);
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateSubData(Uint targetIndex, Uint level, DataPtr input) {
|
void UpdateSubData(Uint targetIndex, Uint level, DataPtr input) {
|
||||||
MOBILEGL_ASSERT(targetIndex < TargetCount, "UpdateSubData: target invalid");
|
MOBILEGL_ASSERT(targetIndex < TargetCount, "UpdateSubData: target invalid");
|
||||||
m_storage[targetIndex].UpdateSubData(level, input);
|
m_storage[targetIndex].UpdateSubData(level, input);
|
||||||
|
|||||||
@@ -271,6 +271,10 @@ namespace MobileGL {
|
|||||||
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureObjectWithOneMipmap::TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) {
|
||||||
|
m_textureStorage.TruncateToLevelCount(GetIndexOfTextureUploadTarget(uploadTarget), levelCount);
|
||||||
|
}
|
||||||
|
|
||||||
void TextureObjectWithOneMipmap::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
void TextureObjectWithOneMipmap::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||||
DataPtr input) {
|
DataPtr input) {
|
||||||
m_textureStorage.UpdateSubData(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
m_textureStorage.UpdateSubData(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||||
|
|||||||
@@ -134,6 +134,10 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
virtual const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const = 0;
|
virtual const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const = 0;
|
||||||
virtual const SizeT GetMipmapByteSize(TextureUploadTarget target, Uint mipmapLevel) const = 0;
|
virtual const SizeT GetMipmapByteSize(TextureUploadTarget target, Uint mipmapLevel) const = 0;
|
||||||
virtual void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) = 0;
|
virtual void AllocateStorage(TextureUploadTarget uploadTarget, Uint mipmapLevel, MipmapInput input) = 0;
|
||||||
|
// AllocateStorage only ever grows the chain. Callers that define the complete level set -
|
||||||
|
// glTexStorage*, mip regeneration, or a level-0 respecification at a new size - drop the
|
||||||
|
// leftovers explicitly, so a stale tail can never make the texture silently incomplete.
|
||||||
|
virtual void TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) = 0;
|
||||||
virtual void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) = 0;
|
virtual void UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) = 0;
|
||||||
virtual void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) = 0;
|
virtual void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) = 0;
|
||||||
virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty = true) = 0;
|
virtual void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty = true) = 0;
|
||||||
@@ -175,6 +179,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
||||||
const SizeT GetMipmapByteSize(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 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 UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) override;
|
||||||
void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override;
|
void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override;
|
||||||
void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) override;
|
void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty) override;
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ namespace MobileGL {
|
|||||||
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureObject2DCube::TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) {
|
||||||
|
m_textureStorage.TruncateToLevelCount(GetIndexOfTextureUploadTarget(uploadTarget), levelCount);
|
||||||
|
}
|
||||||
|
|
||||||
void TextureObject2DCube::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
void TextureObject2DCube::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||||
DataPtr input) {
|
DataPtr input) {
|
||||||
m_textureStorage.UpdateSubData(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
m_textureStorage.UpdateSubData(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace MobileGL {
|
|||||||
const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
const IntVec3 GetMipmapTexelSize(TextureUploadTarget target, Uint mipmapLevel) const override;
|
||||||
const SizeT GetMipmapByteSize(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 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 UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel, DataPtr input) override;
|
||||||
void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override;
|
void* MapMipmapData(TextureUploadTarget uploadTarget, Uint mipmapLevel) override;
|
||||||
void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override;
|
void MarkStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, bool dirty) override;
|
||||||
|
|||||||
@@ -1427,6 +1427,95 @@ TEST_F(TextureTest, TextureStorage1DAndSubImageModifyNamedObjectOnly) {
|
|||||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Building a mip chain top-down - upload level N, then level 0 - must not destroy the levels
|
||||||
|
// already uploaded. AllocateLevel used to resize() the storage down to level+1 on every call, so
|
||||||
|
// the level-0 upload truncated the chain to a single level; the higher level then read back as
|
||||||
|
// {0,0,0}, IsComplete() rejected the zero-then-nonzero pattern, and DirectGLES answered that by
|
||||||
|
// skipping the texture's sync entirely. This is the shape KHR-GL33.texture_repeat_mode uses, and
|
||||||
|
// it accounted for 108 CTS failures in every GL version.
|
||||||
|
TEST_F(TextureTest, TexImage2DOnLevelZeroKeepsAnAlreadyUploadedHigherLevel) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 49, 23, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 98, 46, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
ASSERT_NE(mipmapObject, nullptr);
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapLevelCount(), 2u);
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 0), IntVec3(98, 46, 1));
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 1), IntVec3(49, 23, 1));
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The other half of the contract: respecifying a level 0 that already held an image still drops
|
||||||
|
// the chain, exactly as before. Minecraft rebinds the block-atlas name and calls glTexImage2D on
|
||||||
|
// level 0 before uploading the new levels; leaving the previous chain in place would strand a tail
|
||||||
|
// at the wrong sizes and - because Mojang terminates its chains with a 0x0 level - reproduce the
|
||||||
|
// same incomplete-texture black atlas the fix above exists to prevent.
|
||||||
|
TEST_F(TextureTest, TexImage2DRespecifyingAnExistingLevelZeroDropsTheStaleChain) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
ASSERT_NE(mipmapObject, nullptr);
|
||||||
|
ASSERT_EQ(mipmapObject->GetMipmapLevelCount(), 3u);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapLevelCount(), 1u);
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 0), IntVec3(16, 16, 1));
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same-size respecification has to drop the chain too. The Mipmap Levels video setting rebuilds
|
||||||
|
// the atlas at identical dimensions with a different level count, so a size-change-only test would
|
||||||
|
// let the old tail survive.
|
||||||
|
TEST_F(TextureTest, TexImage2DRespecifyingLevelZeroAtTheSameSizeStillDropsTheChain) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
ASSERT_NE(mipmapObject, nullptr);
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapLevelCount(), 1u);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// glTexStorage2D defines exactly `levels` levels. AllocateStorage only grows now, so the immutable
|
||||||
|
// path has to drop a longer pre-existing chain explicitly.
|
||||||
|
TEST_F(TextureTest, TexStorage2DTrimsALongerPreExistingMipChain) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::TexStorage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 8, 8);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
ASSERT_NE(mipmapObject, nullptr);
|
||||||
|
EXPECT_EQ(mipmapObject->GetMipmapLevelCount(), 2u);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TextureTest, TextureStorage3DAndSubImageModifyNamedObjectOnly) {
|
TEST_F(TextureTest, TextureStorage3DAndSubImageModifyNamedObjectOnly) {
|
||||||
GLuint texture = 0;
|
GLuint texture = 0;
|
||||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_3D, 1, &texture);
|
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_3D, 1, &texture);
|
||||||
|
|||||||
Reference in New Issue
Block a user