Merge branch "feat/log-level-semantics" into dev

This commit is contained in:
2026-08-13 02:19:26 -04:00
55 changed files with 940 additions and 590 deletions
+13 -3
View File
@@ -52,11 +52,15 @@
// that includes Defines.h without Log.h both tokens would silently evaluate to 0 in the
// preprocessor conditional - enabling the assert in exactly the INFO-level builds it is
// documented to be compiled out of. Log.h redefines them identically, which is legal.
//
// Severity order, ascending: DEBUG < INFO < WARN < ERROR < FATAL. MOBILEGL_LOG_ACTIVE_LEVEL
// names the lowest severity compiled in, so the production default INFO keeps I/W/E/F and
// drops only D. Any edit here must be mirrored in Log.h.
#ifndef MOBILEGL_LOG_LEVEL_DEBUG
#define MOBILEGL_LOG_LEVEL_DEBUG 0
#define MOBILEGL_LOG_LEVEL_WARN 1
#define MOBILEGL_LOG_LEVEL_ERROR 2
#define MOBILEGL_LOG_LEVEL_INFO 3
#define MOBILEGL_LOG_LEVEL_INFO 1
#define MOBILEGL_LOG_LEVEL_WARN 2
#define MOBILEGL_LOG_LEVEL_ERROR 3
#define MOBILEGL_LOG_LEVEL_FATAL 4
#endif
@@ -91,6 +95,12 @@
#endif
// =============================== Utils ================================ //
// Asserts are live in exactly the builds where MGLOG_D is live, i.e. DEBUG builds only;
// an INFO build (the production default) compiles them out. DEBUG is the lowest severity
// in the ordering above, so "ACTIVE <= DEBUG" is true only for ACTIVE == DEBUG - the same
// gate MGLOG_D uses in Log.h. That equivalence is what makes this gate survive the
// 2026-08-13 renumbering unchanged; the contract is and stays
// "INFO builds: asserts OFF; DEBUG builds: asserts ON".
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
#define MOBILEGL_ASSERT(condition, ...) \
do { \
+74 -74
View File
@@ -261,14 +261,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
drawBuffer->SyncPersistentMappedRange();
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
if (commandOffset + requiredBytes > drawBuffer->GetSize()) {
MGLOG_E("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
MGLOG_E_ONCE("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
return nullptr;
}
return drawBuffer->MappedData() + commandOffset;
}
if (!indirect) {
MGLOG_E("%s skipped: indirect pointer is null", label);
MGLOG_E_ONCE("%s skipped: indirect pointer is null", label);
return nullptr;
}
@@ -341,7 +341,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* backendResource = EnsureBufferResource(obj);
if (!backendResource || backendResource->id == 0) {
MGLOG_E("No backend buffer found for %s binding point %zu.",
MGLOG_E_ONCE("No backend buffer found for %s binding point %zu.",
MG_Util::ConvertGLEnumToString(glTarget).c_str(), i);
continue;
}
@@ -385,7 +385,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* backendResource = EnsureBufferResource(bufferObject);
if (!backendResource || backendResource->id == 0) {
MGLOG_E("No backend buffer found for %s.", MG_Util::ConvertGLEnumToString(glTarget).c_str());
MGLOG_E_ONCE("No backend buffer found for %s.", MG_Util::ConvertGLEnumToString(glTarget).c_str());
return;
}
BindBufferId(glTarget, backendResource->id);
@@ -410,7 +410,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// PBO is not needed since it should be handled in frontend
if (!currentVAOObject) {
MGLOG_E("No VAO is currently bound, cannot sync necessary buffers.");
MGLOG_E_ONCE("No VAO is currently bound, cannot sync necessary buffers.");
return;
}
@@ -643,7 +643,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<GLintptr>(target.start),
static_cast<GLsizeiptr>(size), GL_MAP_READ_BIT);
if (mapped == nullptr) {
MGLOG_E("EndTransformFeedback: failed to map backend buffer %u for capture readback",
MGLOG_E_ONCE("EndTransformFeedback: failed to map backend buffer %u for capture readback",
target.backendId);
continue;
}
@@ -709,7 +709,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<GLsizeiptr>(packedStride * vertices),
GL_MAP_READ_BIT);
if (packed == nullptr) {
MGLOG_E("EndTransformFeedback: failed to map the scatter capture buffer");
MGLOG_E_ONCE("EndTransformFeedback: failed to map the scatter capture buffer");
return;
}
@@ -935,7 +935,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_backendVertexArrayObjects.CollectGarbageIfNeeded();
if (!currentVAOObject || !vaoTwin) {
MGLOG_E("No VAO is currently bound, cannot sync current VAO.");
MGLOG_E_ONCE("No VAO is currently bound, cannot sync current VAO.");
return;
}
@@ -999,7 +999,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glVertexAttribI4uiv(location, currentValue.uintValue.data());
break;
case MG_State::GLState::VertexAttribBaseType::Unsupported:
MGLOG_E("SyncCurrentVertexAttributeValues: program=%u location=%u has no enabled array and its "
MGLOG_E_ONCE("SyncCurrentVertexAttributeValues: program=%u location=%u has no enabled array and its "
"shader input type 0x%x is not supported as a current generic vertex attribute",
program->GetExternalIndex(), location, program->GetAttribType(location));
break;
@@ -1469,7 +1469,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
if (!currentFBO) {
MGLOG_E("No FBO is currently bound, cannot sync current FBO.");
MGLOG_E_ONCE("No FBO is currently bound, cannot sync current FBO.");
continue;
}
@@ -2240,7 +2240,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (twin) {
twin->Bind(target);
} else {
MGLOG_E("No backend FBO found (maybe not synced) for current %s FBO, cannot bind FBO.",
MGLOG_E_ONCE("No backend FBO found (maybe not synced) for current %s FBO, cannot bind FBO.",
(target == FramebufferTarget::Read ? "READ" : "DRAW"));
}
} else {
@@ -2844,7 +2844,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
(GLintptr)range.start, (GLintptr)(range.end - range.start));
}
} else {
MGLOG_E("No backend buffer found for UBO binding, cannot bind UBO.");
MGLOG_E_ONCE("No backend buffer found for UBO binding, cannot bind UBO.");
}
}
}
@@ -2976,7 +2976,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else {
g_GLESFuncs.glUseProgram(0);
PrgramImpl::g_lastUsedBackendProgramId = 0;
MGLOG_E("No backend program found (maybe not synced) for current program, cannot use program.");
MGLOG_E_ONCE("No backend program found (maybe not synced) for current program, cannot use program.");
}
}
}
@@ -3213,13 +3213,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLuint GetBackendProgramId(GLuint program) {
if (!MG_State::pGLContext->ValidateProgramName(program)) {
MGLOG_E("Invalid frontend program object: %u", program);
MGLOG_E_ONCE("Invalid frontend program object: %u", program);
return 0;
}
auto& programObject = MG_State::pGLContext->GetProgramObject(program);
if (!programObject) {
MGLOG_E("Program object %u is null.", program);
MGLOG_E_ONCE("Program object %u is null.", program);
return 0;
}
@@ -3486,7 +3486,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
stride = sizeof(DrawElementsIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawElementsIndirectCommand))) {
MGLOG_E("MultiDrawElementsIndirect skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawElementsIndirect skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawElementsIndirectCommand));
return;
}
@@ -3496,7 +3496,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("MultiDrawElementsIndirect skipped: unsupported index type 0x%x", type);
MGLOG_E_ONCE("MultiDrawElementsIndirect skipped: unsupported index type 0x%x", type);
return;
}
@@ -3526,7 +3526,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
stride = sizeof(DrawElementsIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawElementsIndirectCommand))) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawElementsIndirectCommand));
return;
}
@@ -3536,18 +3536,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type);
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type);
return;
}
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
if (!drawBuffer) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
return;
}
if (!parameterBuffer) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
return;
}
@@ -3558,11 +3558,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) +
sizeof(DrawElementsIndirectCommand);
if (commandBytes > drawBuffer->GetSize()) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
return;
}
if (drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
return;
}
@@ -3570,7 +3570,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// have - MappedData() is null there and the reads below would be a null dereference,
// not a wrong picture. The DirectVulkan twin declines the same way.
if (parameterBuffer->MappedData() == nullptr || drawBuffer->MappedData() == nullptr) {
MGLOG_E("MultiDrawElementsIndirectCount skipped: CPU fallback cannot read the parameter or "
MGLOG_E_ONCE("MultiDrawElementsIndirectCount skipped: CPU fallback cannot read the parameter or "
"draw-indirect buffer");
return;
}
@@ -3594,7 +3594,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
stride = sizeof(DrawArraysIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
MGLOG_E("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawArraysIndirectCommand));
return;
}
@@ -3634,7 +3634,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
stride = sizeof(DrawArraysIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawArraysIndirectCommand));
return;
}
@@ -3645,11 +3645,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
if (!drawBuffer) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
return;
}
if (!parameterBuffer) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
return;
}
@@ -3660,17 +3660,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) +
sizeof(DrawArraysIndirectCommand);
if (commandBytes > drawBuffer->GetSize()) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
return;
}
if (drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
return;
}
// See the indexed twin: no CPU shadow means no count to read, not a wrong one.
if (parameterBuffer->MappedData() == nullptr || drawBuffer->MappedData() == nullptr) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read the parameter or "
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read the parameter or "
"draw-indirect buffer");
return;
}
@@ -3763,7 +3763,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
MGLOG_E_ONCE("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
return;
}
@@ -3960,7 +3960,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLuint>(previousDraw));
FramebufferImpl::InvalidateFramebufferBindingCache();
if (!resolved) {
MGLOG_E("BlitFramebuffer: multisample resolve fallback failed");
MGLOG_E_ONCE("BlitFramebuffer: multisample resolve fallback failed");
}
return resolved;
}
@@ -4259,7 +4259,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
s_stencilProgram = BuildProgram(kStencilFragmentSource);
if (s_depthProgram == 0 || s_stencilProgram == 0) {
s_programsFailed = true;
MGLOG_E("BlitFramebuffer: could not build the multisample replicate programs");
MGLOG_E_ONCE("BlitFramebuffer: could not build the multisample replicate programs");
return false;
}
s_depthUvTransform = g_GLESFuncs.glGetUniformLocation(s_depthProgram, "uUvTransform");
@@ -4463,7 +4463,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(previousRead));
FramebufferImpl::InvalidateFramebufferBindingCache();
if (!ok) {
MGLOG_E("BlitFramebuffer: could not stage the source for the multisample replicate");
MGLOG_E_ONCE("BlitFramebuffer: could not stage the source for the multisample replicate");
return false;
}
@@ -4532,7 +4532,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Everything the pass disturbed goes back through emulationState's destructor.
if (!replicated) {
MGLOG_E("BlitFramebuffer: multisample replicate fallback failed");
MGLOG_E_ONCE("BlitFramebuffer: multisample replicate fallback failed");
}
return replicated;
}
@@ -4567,7 +4567,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// outright, desktop GL replicates the source sample into every destination one.
if (ReplicateBlitIntoMultisampleDraw(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask)) {
if ((mask & GL_COLOR_BUFFER_BIT) != 0) {
MGLOG_E("BlitFramebuffer: colour replicate into a multisample draw framebuffer is not emulated");
MGLOG_E_ONCE("BlitFramebuffer: colour replicate into a multisample draw framebuffer is not emulated");
}
}
return;
@@ -4668,7 +4668,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
if (!TextureImpl::IsSupportedTextureTarget(textureTarget)) {
MGLOG_E(" Texture target %s is not supported, skipping.",
MGLOG_E_ONCE(" Texture target %s is not supported, skipping.",
MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
return false;
}
@@ -4677,7 +4677,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
{
const auto& textureObject = bindingSlot.GetBoundObject();
if (!textureObject) {
MGLOG_W("%s: Texture target %s does not have texture bound.", __func__,
MGLOG_D("%s: Texture target %s does not have texture bound.", __func__,
MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
}
@@ -4926,7 +4926,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true;
}
MGLOG_E("%s failed: %s. target=%s, format=%s", operation,
MGLOG_E_ONCE("%s failed: %s. target=%s, format=%s", operation,
MG_Util::ConvertGLEnumToString(err).c_str(),
MG_Util::ConvertGLEnumToString(target).c_str(),
MG_Util::ConvertTextureInternalFormatToString(format).c_str());
@@ -5262,7 +5262,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
.GetBoundObject();
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get());
if (!backendTextureSlot || !*backendTextureSlot) {
MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.",
MGLOG_E_ONCE("CopyTexSubImage2D: No backend texture found for texture %u.",
textureObject ? textureObject->GetExternalIndex() : 0);
return;
}
@@ -5313,7 +5313,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<Uint>(currentTex), target, level, isStencilFormat);
if (g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE");
MGLOG_E_ONCE("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE");
return;
}
@@ -5357,7 +5357,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
.GetBoundObject();
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get());
if (!backendTextureSlot || !*backendTextureSlot) {
MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.",
MGLOG_E_ONCE("CopyTexSubImage2D: No backend texture found for texture %u.",
textureObject ? textureObject->GetExternalIndex() : 0);
return;
}
@@ -5395,7 +5395,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
});
if (g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
MGLOG_E("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE");
MGLOG_E_ONCE("ES glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE");
return;
}
@@ -6002,7 +6002,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
const SizeT pboOffset = reinterpret_cast<SizeT>(pixels);
if (pixelPackBufferObject && pboOffset + packedSize > pixelPackBufferObject->GetSize()) {
MGLOG_E("ReadPixels: %s readback PBO is too small", what);
MGLOG_E_ONCE("ReadPixels: %s readback PBO is too small", what);
return false;
}
Vector<Uint8> rowBuf(rowBytes);
@@ -6156,7 +6156,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
s_stencilProgram = ReplicateBlitImpl::BuildProgram(kStencilFetchFragmentSource);
if (s_depthProgram == 0 || s_stencilProgram == 0) {
s_programsFailed = true;
MGLOG_E("ReadPixels: could not build the depth/stencil readback programs");
MGLOG_E_ONCE("ReadPixels: could not build the depth/stencil readback programs");
return false;
}
s_depthUvTransform = g_GLESFuncs.glGetUniformLocation(s_depthProgram, "uUvTransform");
@@ -6414,7 +6414,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ClearGLErrors();
g_GLESFuncs.glDrawArrays(GL_TRIANGLES, 0, 3);
if (g_GLESFuncs.glGetError() != GL_NO_ERROR) {
MGLOG_E("ReadPixels: the %s conversion pass failed", stencilAspect ? "stencil" : "depth");
MGLOG_E_ONCE("ReadPixels: the %s conversion pass failed", stencilAspect ? "stencil" : "depth");
return false;
}
@@ -6430,7 +6430,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glReadPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, outWords.data());
const GLenum readError = g_GLESFuncs.glGetError();
if (readError != GL_NO_ERROR) {
MGLOG_E("ReadPixels: could not read the %s conversion target back: %s",
MGLOG_E_ONCE("ReadPixels: could not read the %s conversion target back: %s",
stencilAspect ? "stencil" : "depth", MG_Util::ConvertGLEnumToString(readError).c_str());
return false;
}
@@ -6450,20 +6450,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferImpl::BindFramebufferId(GL_DRAW_FRAMEBUFFER, slot.framebuffer);
if (!StageAspect(slot, candidates, stencilAspect, isDefault, x, y, width, height)) {
MGLOG_E("ReadPixels: no ES-compatible scratch format for the %s source",
MGLOG_E_ONCE("ReadPixels: no ES-compatible scratch format for the %s source",
stencilAspect ? "stencil" : "depth");
return false;
}
if (!EnsureColorTexture(width, height)) {
MGLOG_E("ReadPixels: could not allocate the depth/stencil conversion target");
MGLOG_E_ONCE("ReadPixels: could not allocate the depth/stencil conversion target");
return false;
}
FramebufferImpl::BindFramebufferId(GL_DRAW_FRAMEBUFFER, s_colorFramebuffer);
g_GLESFuncs.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
s_colorTexture, 0);
if (g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
MGLOG_E("ReadPixels: the depth/stencil conversion target is not renderable");
MGLOG_E_ONCE("ReadPixels: the depth/stencil conversion target is not renderable");
return false;
}
@@ -6568,7 +6568,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (DepthStencilSamplingReadImpl::Read(x, y, width, height, &outDepth, /*outStencil=*/nullptr)) {
return true;
}
MGLOG_E("ReadPixels: no depth readback path is available: native reads failed with %s and the "
MGLOG_E_ONCE("ReadPixels: no depth readback path is available: native reads failed with %s and the "
"sampling emulation could not service the source",
MG_Util::ConvertGLEnumToString(floatError).c_str());
return false;
@@ -6672,7 +6672,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (DepthStencilSamplingReadImpl::Read(x, y, width, height, /*outDepth=*/nullptr, &outStencil)) {
return true;
}
MGLOG_E("ReadPixels: no stencil readback path is available: native reads failed with %s and the "
MGLOG_E_ONCE("ReadPixels: no stencil readback path is available: native reads failed with %s and the "
"sampling emulation could not service the source",
MG_Util::ConvertGLEnumToString(packedError).c_str());
return false;
@@ -6977,7 +6977,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool integerAttachment =
attachmentComponentType == GL_INT || attachmentComponentType == GL_UNSIGNED_INT;
if (mapping.isInteger != integerAttachment) {
MGLOG_E("Readback conversion: integer-ness of format %s does not match the read buffer, skipping",
MGLOG_E_ONCE("Readback conversion: integer-ness of format %s does not match the read buffer, skipping",
MG_Util::ConvertGLEnumToString(format).c_str());
return true;
}
@@ -7053,7 +7053,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
if (wideType == GL_NONE) {
MGLOG_E("Readback conversion: ES accepted no wide read type for format %s type %s, skipping readback",
MGLOG_E_ONCE("Readback conversion: ES accepted no wide read type for format %s type %s, skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str());
return true;
}
@@ -7236,7 +7236,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool convertible = GetReadbackChannelMapping(format, conversionMapping) &&
GetReadbackDstPixelSize(conversionMapping, type) != 0;
if (!useNativeReadback && !convertible) {
MGLOG_E("ReadPixels: format %s with type %s is not implemented yet, skipping readback",
MGLOG_E_ONCE("ReadPixels: format %s with type %s is not implemented yet, skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str());
return;
}
@@ -7257,7 +7257,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ReadPixels: GL_READ_FRAMEBUFFER status = %s", MG_Util::ConvertGLEnumToString(fbStatus).c_str());
if (fbStatus != GL_FRAMEBUFFER_COMPLETE) {
MGLOG_E("ReadPixels: bound READ FBO is not complete");
MGLOG_E_ONCE("ReadPixels: bound READ FBO is not complete");
return;
}
// ES only guarantees GL_RGBA/GL_UNSIGNED_BYTE and GL_RGBA_INTEGER/GL_(UNSIGNED_)INT for the
@@ -7282,7 +7282,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ReadPixels: finished via client-format conversion");
return;
}
MGLOG_E("ReadPixels: format %s with type %s is not implemented yet, skipping readback",
MGLOG_E_ONCE("ReadPixels: format %s with type %s is not implemented yet, skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str());
return;
}
@@ -7315,7 +7315,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* backendResource = BufferImpl::EnsureBufferResource(pixelPackBufferObject);
MGLOG_D("ReadPixels: Using PBO %u", pixelPackBufferObject->GetExternalIndex());
if (!backendResource || backendResource->id == 0) {
MGLOG_E("ReadPixels: No backend buffer found for PBO %u.",
MGLOG_E_ONCE("ReadPixels: No backend buffer found for PBO %u.",
pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0);
return;
}
@@ -7347,7 +7347,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ReadPixels: finished via client-format conversion after native failure");
return;
}
MGLOG_E("ReadPixels: native read of %s/%s failed (%s) and no conversion path covers it, "
MGLOG_E_ONCE("ReadPixels: native read of %s/%s failed (%s) and no conversion path covers it, "
"skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(),
MG_Util::ConvertGLEnumToString(nativeReadError).c_str());
@@ -7367,7 +7367,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ReadPixels: Unmapping PBO");
g_GLESFuncs.glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
} else {
MGLOG_E("ReadPixels: glMapBufferRange returned nullptr");
MGLOG_E_ONCE("ReadPixels: glMapBufferRange returned nullptr");
}
}
MGLOG_D("ReadPixels: finished");
@@ -7403,7 +7403,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const Bool convertible = GetReadbackChannelMapping(format, conversionMapping) &&
GetReadbackDstPixelSize(conversionMapping, type) != 0;
if (!useNativeReadback && !convertible) {
MGLOG_E("GetTexImage: format %s with type %s is not implemented yet, skipping readback",
MGLOG_E_ONCE("GetTexImage: format %s with type %s is not implemented yet, skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str());
return;
}
@@ -7431,7 +7431,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.Find(textureObject.get());
if (!backendTextureSlot || !*backendTextureSlot) {
MGLOG_E("GetTexImage: No backend texture found for texture %u.",
MGLOG_E_ONCE("GetTexImage: No backend texture found for texture %u.",
textureObject ? textureObject->GetExternalIndex() : 0);
return;
}
@@ -7499,7 +7499,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("GetTexImage: texture storage type = %d", (int)storageType);
if (storageType == TextureStorageType::Buffer) {
MGLOG_E("GetTexImage: Texture storage type Buffer is not supported.");
MGLOG_E_ONCE("GetTexImage: Texture storage type Buffer is not supported.");
return;
}
@@ -7511,7 +7511,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// levelRange.y() is GL_TEXTURE_MAX_LEVEL, an inclusive level index — a single-level
// texture has range [0, 0] and level 0 must be readable.
if (static_cast<Uint>(level) < levelRange.x() || static_cast<Uint>(level) > levelRange.y()) {
MGLOG_E("GetTexImage: Requested level %d is out of range (base level %u, max level %u), skipping readback",
MGLOG_E_ONCE("GetTexImage: Requested level %d is out of range (base level %u, max level %u), skipping readback",
level, levelRange.x(), levelRange.y());
return;
}
@@ -7608,15 +7608,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
return;
}
if (!tempFBOComplete) {
MGLOG_E("GetTexImage: READ FBO incomplete and no shadow copy available, skipping readback");
MGLOG_E_ONCE("GetTexImage: READ FBO incomplete and no shadow copy available, skipping readback");
return;
}
MGLOG_E("GetTexImage: format %s with type %s is not implemented yet, skipping readback",
MGLOG_E_ONCE("GetTexImage: format %s with type %s is not implemented yet, skipping readback",
MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str());
return;
}
if (!tempFBOComplete) {
MGLOG_E("GetTexImage: bound READ FBO is not complete");
MGLOG_E_ONCE("GetTexImage: bound READ FBO is not complete");
return;
}
@@ -7644,7 +7644,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* backendResource = BufferImpl::EnsureBufferResource(pixelPackBufferObject);
MGLOG_D("GetTexImage: Using PBO %u", pixelPackBufferObject->GetExternalIndex());
if (!backendResource || backendResource->id == 0) {
MGLOG_E("GetTexImage: No backend buffer found for PBO %u.",
MGLOG_E_ONCE("GetTexImage: No backend buffer found for PBO %u.",
pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0);
return;
}
@@ -7680,7 +7680,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ReadPixels: Unmapping PBO");
g_GLESFuncs.glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
} else {
MGLOG_E("ReadPixels: glMapBufferRange returned nullptr");
MGLOG_E_ONCE("ReadPixels: glMapBufferRange returned nullptr");
}
}
@@ -8067,7 +8067,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (g_requestedSwapInterval < 0) return;
if (!g_EGLFuncs.eglSwapInterval || g_Display == EGL_NO_DISPLAY || g_Surface == EGL_NO_SURFACE) return;
const EGLBoolean ok = g_EGLFuncs.eglSwapInterval(g_Display, g_requestedSwapInterval);
MGLOG_I("DirectGLES: applied native swap interval %d (%s)", g_requestedSwapInterval,
MGLOG_D("DirectGLES: applied native swap interval %d (%s)", g_requestedSwapInterval,
ok ? "ok" : "failed");
}
} // namespace
@@ -8178,12 +8178,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool MakeCurrent() {
if (!g_EGLFuncs.eglMakeCurrent || g_Display == EGL_NO_DISPLAY || g_Surface == EGL_NO_SURFACE ||
g_Context == EGL_NO_CONTEXT) {
MGLOG_E("DirectGLES::MakeCurrent failed: EGL display/surface/context is not initialized");
MGLOG_E_ONCE("DirectGLES::MakeCurrent failed: EGL display/surface/context is not initialized");
return false;
}
if (!g_EGLFuncs.eglMakeCurrent(g_Display, g_Surface, g_Surface, g_Context)) {
const EGLint error = g_EGLFuncs.eglGetError ? g_EGLFuncs.eglGetError() : EGL_SUCCESS;
MGLOG_E("DirectGLES::MakeCurrent failed: native eglMakeCurrent returned error 0x%04x", error);
MGLOG_E_ONCE("DirectGLES::MakeCurrent failed: native eglMakeCurrent returned error 0x%04x", error);
return false;
}
InvalidateEglVerifiedStamp();
@@ -8216,7 +8216,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
if (!g_EGLFuncs.eglMakeCurrent(g_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
const EGLint error = g_EGLFuncs.eglGetError ? g_EGLFuncs.eglGetError() : EGL_SUCCESS;
MGLOG_E("DirectGLES::ReleaseCurrent failed: native eglMakeCurrent returned error 0x%04x", error);
MGLOG_E_ONCE("DirectGLES::ReleaseCurrent failed: native eglMakeCurrent returned error 0x%04x", error);
return false;
}
// Clearing the global owner works from ANY thread (a release request can
+76 -78
View File
@@ -598,7 +598,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void* ptr = g_GLESFuncs.glMapBufferRange(TempBufferTarget, 0, static_cast<GLsizeiptr>(size),
GL_MAP_WRITE_BIT | kMapPersistentBit | kMapCoherentBit);
if (!ptr) {
MGLOG_E("Ops_AcquirePersistentMap: glMapBufferRange(persistent) failed for buffer %u",
MGLOG_E_ONCE("Ops_AcquirePersistentMap: glMapBufferRange(persistent) failed for buffer %u",
resource->id);
resource->persistentMapped = false;
resource->persistentPtr = nullptr;
@@ -716,7 +716,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
resource->syncedChangeSerial = bufferObject.GetChangeSerial();
return;
}
MGLOG_E("Failed to map buffer with ID: %u for flush, falling back to glBufferSubData",
MGLOG_E_ONCE("Failed to map buffer with ID: %u for flush, falling back to glBufferSubData",
resource->id);
}
UploadRangeNow(*resource, bufferObject, range.start, range.end);
@@ -739,7 +739,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void* mapped = g_GLESFuncs.glMapBufferRange(TempBufferTarget, 0, static_cast<GLsizeiptr>(size),
GL_MAP_READ_BIT);
if (mapped == nullptr) {
MGLOG_E("Ops_ReadbackFromGpu: glMapBufferRange(read) failed for buffer %u", resource->id);
MGLOG_E_ONCE("Ops_ReadbackFromGpu: glMapBufferRange(read) failed for buffer %u", resource->id);
return;
}
bufferObject.WritebackFromBackend({mapped, size}, 0);
@@ -993,8 +993,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else {
g_GLESFuncs.glGenBuffers(1, &resource->id);
if (resource->id == 0) {
MGLOG_E("Failed to generate buffer object.");
MGLOG_E("ES glGetError(): %s",
MGLOG_E_ONCE("Failed to generate buffer object.");
MGLOG_E_ONCE("ES glGetError(): %s",
MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
return resource;
}
@@ -1224,7 +1224,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
if (id == 0) {
MGLOG_E("Global-UBO ring: persistent storage creation failed (%zu bytes); "
MGLOG_E_ONCE("Global-UBO ring: persistent storage creation failed (%zu bytes); "
"falling back to glBufferSubData uploads.",
newSize);
g_uboRing.creationFailed = true;
@@ -1470,8 +1470,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_clientAttributeBufferIds.fill(0);
g_GLESFuncs.glGenVertexArrays(1, &m_backendVAOId);
if (m_backendVAOId == 0) {
MGLOG_E("Failed to generate vertex array object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to generate vertex array object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Generated vertex array object with ID: %u.", m_backendVAOId);
}
@@ -1529,13 +1529,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
inline Bool BindAttributeBuffer(const MG_State::GLState::VertexAttribute& attrib) {
const auto& bufferObject = attrib.Buffer;
if (!bufferObject) {
MGLOG_W("Attribute has no bound buffer, skipping.");
MGLOG_W_ONCE("Attribute has no bound buffer, skipping.");
return false;
}
auto* backendResource = BufferImpl::EnsureBufferResource(bufferObject);
if (!backendResource || backendResource->id == 0) {
MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute.");
MGLOG_E_ONCE("No backend buffer found for attribute's buffer, cannot bind attribute.");
return false;
}
@@ -1586,12 +1586,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
inline Bool SyncZeroStrideAttribute(Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib) {
const auto& bufferObject = attrib.Buffer;
if (!bufferObject) {
MGLOG_W("Zero-stride attribute %u has no bound buffer, skipping.", attribIndex);
MGLOG_W_ONCE("Zero-stride attribute %u has no bound buffer, skipping.", attribIndex);
return false;
}
auto* backendResource = BufferImpl::EnsureBufferResource(bufferObject);
if (!backendResource || backendResource->id == 0) {
MGLOG_E("No backend buffer for zero-stride attribute %u, cannot bind it.", attribIndex);
MGLOG_E_ONCE("No backend buffer for zero-stride attribute %u, cannot bind it.", attribIndex);
return false;
}
@@ -1621,7 +1621,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (!stateVAOObject) {
MGLOG_E("State VAO object is null, cannot sync to backend.");
MGLOG_E_ONCE("State VAO object is null, cannot sync to backend.");
return;
}
@@ -1694,7 +1694,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// because the array stayed enabled with no pointer the failed call could set.
// The type test therefore covers the storage, not the spelling.
if (attrib.IsLong || attrib.Type == DataType::Float64) {
MGLOG_I("DirectGLES: vertex attribute %u is a 64-bit (GL_DOUBLE) array, which this "
MGLOG_W_ONCE("DirectGLES: vertex attribute %u is a 64-bit (GL_DOUBLE) array, which this "
"backend cannot feed - disabling the array",
attribIndex);
g_GLESFuncs.glDisableVertexAttribArray(attribIndex);
@@ -1761,7 +1761,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
if (formatMayBeRefused && g_GLESFuncs.glGetError() != GL_NO_ERROR) {
MGLOG_I("DirectGLES: the driver refused the vertex format of attribute %u "
MGLOG_W_ONCE("DirectGLES: the driver refused the vertex format of attribute %u "
"(size=%d bgra=%d type=%s) - disabling the array so the draw cannot "
"fetch through a pointer the driver never accepted",
attribIndex, attrib.Size, attrib.IsBgra ? 1 : 0,
@@ -1784,7 +1784,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
BufferImpl::BindBufferId(GL_ELEMENT_ARRAY_BUFFER, backendResource->id);
indexBufferSynced = true;
} else {
MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer.");
MGLOG_W_ONCE("No backend buffer found for index buffer binding, cannot bind index buffer.");
}
} else {
g_GLESFuncs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -1842,7 +1842,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (bufferId == 0) {
g_GLESFuncs.glGenBuffers(1, &bufferId);
if (bufferId == 0) {
MGLOG_E("Failed to create client-side vertex attribute upload buffer.");
MGLOG_E_ONCE("Failed to create client-side vertex attribute upload buffer.");
continue;
}
}
@@ -1877,8 +1877,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGenTextures(1, &m_backendTextureId);
m_contextGeneration = g_backendContextGeneration;
if (m_backendTextureId == 0) {
MGLOG_E("Failed to generate texture object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to generate texture object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Generated texture object with ID: %u.", m_backendTextureId);
}
@@ -1956,8 +1956,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGenTextures(1, &m_backendTextureId);
m_contextGeneration = g_backendContextGeneration;
if (m_backendTextureId == 0) {
MGLOG_E("Failed to regenerate texture object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to regenerate texture object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Regenerated texture object with ID: %u.", m_backendTextureId);
}
@@ -2391,7 +2391,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void BackendTextureObject::SyncMipmapsToBackend(
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
MGLOG_E_ONCE("State texture object is null, cannot sync to backend.");
return;
}
@@ -2424,7 +2424,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D(" Texture target for syncing is %s",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
if (!IsSupportedTextureTarget(targetInternal)) {
MGLOG_E(" Texture target %s is not supported, skipping.",
MGLOG_E_ONCE(" Texture target %s is not supported, skipping.",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
return;
}
@@ -2576,7 +2576,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<GLsizei>(uploadSize.z()), 0, glFormat, glType, uploadData);
break;
default:
MGLOG_E("Unhandled texture target %s",
MGLOG_E_ONCE("Unhandled texture target %s",
MG_Util::ConvertTextureTargetToString(stateTextureObject->GetTarget()).c_str());
break;
}
@@ -2654,7 +2654,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<GLsizei>(storageSize.z()));
break;
default:
MGLOG_E("Unhandled immutable texture target %s",
MGLOG_E_ONCE("Unhandled immutable texture target %s",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
break;
}
@@ -2776,7 +2776,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
break;
}
default: {
MGLOG_E("Unhandled texture target %s",
MGLOG_E_ONCE("Unhandled texture target %s",
MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
}
}
@@ -2828,7 +2828,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto byteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level);
if (byteSize == 0) {
MGLOG_W("Mipmap level %d has no data, skipping update.", level);
MGLOG_D("Mipmap level %d has no data, skipping update.", level);
continue;
}
@@ -2979,7 +2979,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
break;
default:
MGLOG_E("Unhandled texture target %s",
MGLOG_E_ONCE("Unhandled texture target %s",
MG_Util::ConvertTextureTargetToString(stateTextureObject->GetTarget()).c_str());
break;
}
@@ -3007,7 +3007,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Need to sync texture buffer if not synced yet
auto* backendBufferResource = BufferImpl::EnsureBufferResource(buffer);
if (!backendBufferResource || backendBufferResource->id == 0) {
MGLOG_E("Failed to sync backing buffer for texture buffer with ID: %u",
MGLOG_E_ONCE("Failed to sync backing buffer for texture buffer with ID: %u",
stateTextureObject->GetExternalIndex());
return;
}
@@ -3026,15 +3026,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
// below that without EXT/OES_texture_buffer. Calling it was an unconditional
// null dereference. There is no conformant way to refuse the call (it is valid
// in the context MobileGL claims), so the texture is left unbacked and the
// reason is stated once per respecify at a level that survives the shipped
// INFO build - MGLOG_E is compiled out there, which is exactly how this class
// of defect stays invisible.
// reason is stated once per object, latched by the flag below. It was parked
// at MGLOG_I while the level ordering compiled MGLOG_W out of INFO builds;
// W is the correct level and now survives there.
if (!AreBufferTexturesSupported()) {
if (m_bufferTextureUnsupportedReported) {
break;
}
m_bufferTextureUnsupportedReported = true;
MGLOG_I("Texture buffer %u cannot be backed: this ES driver has no buffer "
MGLOG_W("Texture buffer %u cannot be backed: this ES driver has no buffer "
"textures (%s). Every draw sampling it will read zero and every "
"shader declaring a samplerBuffer will fail to compile. MobileGL "
"still advertises GL_MAX_TEXTURE_BUFFER_SIZE = %d because an "
@@ -3062,7 +3062,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else if (!CallTexBufferRange(GL_TEXTURE_BUFFER, glInternalFormat, backendId,
static_cast<GLintptr>(rangeOffset),
static_cast<GLsizeiptr>(rangeSize))) {
MGLOG_I("Texture buffer %u names a sub-range but the driver has no "
MGLOG_W_ONCE("Texture buffer %u names a sub-range but the driver has no "
"glTexBufferRange; binding the whole buffer instead",
stateTextureObject->GetExternalIndex());
CallTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
@@ -3080,7 +3080,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// TextureStorageType is {Mipmap, Buffer}, both handled above, so this is a
// backstop for a state object that grew a new storage kind. Skipping the upload
// renders wrong; throwing unwinds through the C GL ABI and kills the process.
MGLOG_I("DirectGLES texture sync: no upload path for storage type %d on texture %u; "
MGLOG_E_ONCE("DirectGLES texture sync: no upload path for storage type %d on texture %u; "
"skipping this sync",
static_cast<int>(stateTextureObject->GetStorageType()),
stateTextureObject->GetExternalIndex());
@@ -3115,7 +3115,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#endif
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
MGLOG_E_ONCE("State texture object is null, cannot sync to backend.");
return;
}
@@ -3136,7 +3136,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D(" Texture target for syncing is %s",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
if (!IsSupportedTextureTarget(targetInternal)) {
MGLOG_E(" Texture target %s is not supported, skipping.",
MGLOG_E_ONCE(" Texture target %s is not supported, skipping.",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
return;
}
@@ -3225,7 +3225,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#endif
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
MGLOG_E_ONCE("State texture object is null, cannot sync to backend.");
return;
}
@@ -3245,7 +3245,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D(" Texture target for syncing is %s",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
if (!IsSupportedTextureTarget(targetInternal)) {
MGLOG_E(" Texture target %s is not supported, skipping.",
MGLOG_E_ONCE(" Texture target %s is not supported, skipping.",
MG_Util::ConvertTextureTargetToString(targetInternal).c_str());
return;
}
@@ -3393,8 +3393,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGenFramebuffers(1, &m_backendFBOId);
m_contextGeneration = g_backendContextGeneration;
if (m_backendFBOId == 0) {
MGLOG_E("Failed to generate framebuffer object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to generate framebuffer object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Generated framebuffer object with ID: %u.", m_backendFBOId);
}
@@ -3530,7 +3530,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
backendTextureObject = newTextureSlot;
}
if (!backendTextureObject) {
MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__);
MGLOG_E_ONCE("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__);
return false;
}
backendTextureObject->SyncMipmapsToBackend(textureObject);
@@ -3899,7 +3899,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (!stateFBOObject) {
MGLOG_E("State FBO object is null, cannot sync to backend.");
MGLOG_E_ONCE("State FBO object is null, cannot sync to backend.");
return;
}
MGLOG_D("Syncing FBO with backend ID %u to backend for state ID %u, as %s FBO", m_backendFBOId,
@@ -4418,8 +4418,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
#endif
m_backendProgramId = g_GLESFuncs.glCreateProgram();
if (m_backendProgramId == 0) {
MGLOG_E("Failed to create program object in backend.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to create program object in backend.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Created backend program object with ID: %u", m_backendProgramId);
@@ -4658,7 +4658,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (!stateProgramObject) {
MGLOG_E("State program object is null, skipping backend sync.");
MGLOG_E_ONCE("State program object is null, skipping backend sync.");
return;
}
// Recorded before either early return below, so Use() can always name the GL
@@ -4671,7 +4671,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// a LINK_STATUS it already reported true, so "linked but not drawable" is the
// answer, and this is where the ES backend expresses it.
if (!stateProgramObject->GetLinkStatus() || !stateProgramObject->GetSpirvStatus()) {
MGLOG_E("Program object is not linked or has no generated SPIR-V, skipping backend sync. State "
MGLOG_E_ONCE("Program object is not linked or has no generated SPIR-V, skipping backend sync. State "
"program ID: %u",
stateProgramObject->GetExternalIndex());
return;
@@ -4763,7 +4763,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLuint backendShaderId = g_GLESFuncs.glCreateShader(glShaderType);
if (backendShaderId == 0) {
MGLOG_E("Failed to create backend shader for attachment.");
MGLOG_E_ONCE("Failed to create backend shader for attachment.");
continue;
}
String source;
@@ -4773,12 +4773,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
// ES 3.2 or EXT/OES_texture_buffer on the host. Without it SPIRV-Cross emits
// `#extension GL_EXT_texture_buffer : require` and the driver rejects both that
// and the isamplerBuffer keyword - the program never links and every draw using it
// becomes a silent no-op. Say so here, naming the stage, instead of leaving a
// driver info log the shipped INFO build compiles out (MGLOG_E is inactive there).
// becomes a silent no-op. Say so here, naming the stage. Deliberately unlatched:
// this is bounded by program count, and which stage failed is the whole point.
// Gated on the capability so the module walk never runs on a healthy driver.
if (!AreBufferTexturesSupported() &&
MG_Util::ShaderTranspiler::ShaderCompiler::ModuleDeclaresBufferTextureSampler(spirvCode)) {
MGLOG_I("Program %u stage %s samples a buffer texture, which this ES driver "
MGLOG_E("Program %u stage %s samples a buffer texture, which this ES driver "
"cannot provide (%s). The shader will not compile and the program will "
"not link; every draw using it is a no-op.",
m_backendProgramId,
@@ -4947,14 +4947,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
spvcSession.Compile(&result);
if (!result) {
// MGLOG_I, for the same reason as the compile- and link-failure diagnostics
// below: every CI, retrace and release build compiles at
// MOBILEGL_LOG_LEVEL_INFO, where MGLOG_E expands to nothing. A stage that
// MGLOG_E, unlatched, like the compile- and link-failure diagnostics below:
// one line per failing stage is bounded by program count and naming the
// stage is the entire diagnostic value. A stage that
// never reaches the driver leaves the program short of that stage, so the
// link fails with an EMPTY driver info log - the least debuggable failure
// MobileGL can produce, and what hid the whole
// KHR-GL43.vertex_attrib_binding family behind "the draw captured zeros".
MGLOG_I("Shader transpilation to ESSL failed. State program ID: %u, stage: %s, "
MGLOG_E("Shader transpilation to ESSL failed. State program ID: %u, stage: %s, "
"SPIRV-Cross error: %s",
stateProgramObject->GetExternalIndex(),
MG_Util::ConvertGLEnumToString(glShaderType).c_str(),
@@ -5041,14 +5041,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
Vector<GLchar> log(static_cast<SizeT>(logLength) + 1, '\0');
g_GLESFuncs.glGetShaderInfoLog(backendShaderId, logLength, nullptr, log.data());
log.back() = '\0';
// MGLOG_I, deliberately. Every CI, retrace and release build compiles at
// MOBILEGL_LOG_LEVEL_INFO, where MGLOG_E and MGLOG_W expand to nothing
// (Log.h orders DEBUG < WARN < ERROR < INFO), so this diagnostic used to
// exist only in debug builds: the Android retrace artifact carried 294
// INFO lines and zero ERROR lines while two generated shaders were being
// rejected outright, and the lane could not say why it was rendering an
// empty translucent layer. A shader the driver refuses is never noise.
MGLOG_I("Shader compilation failed. State program ID: %u, stage: %s, backend shader ID: "
// MGLOG_E, unlatched. This was parked at MGLOG_I while the level ordering
// compiled E and W out of every INFO build: the Android retrace artifact
// carried 294 INFO lines and zero ERROR lines while two generated shaders
// were being rejected outright, and the lane could not say why it was
// rendering an empty translucent layer. A shader the driver refuses is
// never noise, and one line per refused shader is bounded by program count.
MGLOG_E("Shader compilation failed. State program ID: %u, stage: %s, backend shader ID: "
"%u, driver log: %s",
stateProgramObject->GetExternalIndex(),
MG_Util::ConvertGLEnumToString(glShaderType).c_str(), backendShaderId,
@@ -5132,7 +5131,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// MGLOG_I for the same reason as the compile failure above: a program that
// links nothing no-ops every draw that uses it, and that has to be readable
// in an INFO-level artifact.
MGLOG_I("Program linking failed. State program ID: %u, backend program ID: %u, driver log: %s",
MGLOG_E("Program linking failed. State program ID: %u, backend program ID: %u, driver log: %s",
stateProgramObject->GetExternalIndex(), m_backendProgramId, log.data());
} else {
MGLOG_D("Program linked successfully. ID: %u", m_backendProgramId);
@@ -5224,7 +5223,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_globalUboBackendBlockSize = static_cast<Int>(blockDataSize);
}
} else {
MGLOG_W("Program %u has frontend global UBO storage, but backend has no %s block.",
MGLOG_W_ONCE("Program %u has frontend global UBO storage, but backend has no %s block.",
stateProgramObject->GetExternalIndex(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME);
}
}
@@ -5300,13 +5299,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
return;
}
if (!m_backendProgramUsable) {
// MGLOG_I, not MGLOG_W: at MOBILEGL_LOG_LEVEL_INFO - the level the shipped
// fordebug builds compile at - only I and F survive, and this is precisely the
// line those builds need. Every draw made with this program renders nothing and
// raises no GL error, so without it the only symptom is a framebuffer that kept
// its clear colour. The early return above keeps it to at most one line per
// program state change, not one per draw.
MGLOG_I("Backend program for GL program %u is unusable (a shader failed to transpile, "
// Every draw made with this program renders nothing and raises no GL error, so
// without this line the only symptom is a framebuffer that kept its clear
// colour. Latched: the early return above only dedupes CONSECUTIVE binds, so an
// app alternating a healthy and a broken program would otherwise log every
// single draw. Parked at MGLOG_I until the level ordering was fixed.
MGLOG_E_ONCE("Backend program for GL program %u is unusable (a shader failed to transpile, "
"compile or link); binding program 0 - draws with it will render nothing",
m_frontendProgramId);
}
@@ -5354,8 +5352,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGenSamplers(1, &m_backendSamplerId);
m_contextGeneration = g_backendContextGeneration;
if (m_backendSamplerId == 0) {
MGLOG_E("Failed to generate sampler object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to generate sampler object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
} else {
MGLOG_D("Generated sampler object with ID: %u.", m_backendSamplerId);
}
@@ -5387,7 +5385,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (!stateSamplerObject) {
MGLOG_E("State sampler object is null, cannot sync to backend.");
MGLOG_E_ONCE("State sampler object is null, cannot sync to backend.");
return;
}
@@ -5498,8 +5496,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glGenRenderbuffers(1, &m_backendRBOId);
m_contextGeneration = g_backendContextGeneration;
if (m_backendRBOId == 0) {
MGLOG_E("Failed to generate renderbuffer object.");
MGLOG_E("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
MGLOG_E_ONCE("Failed to generate renderbuffer object.");
MGLOG_E_ONCE("ES glGetError(): %s", MG_Util::ConvertGLEnumToString(g_GLESFuncs.glGetError()).c_str());
}
}
@@ -5531,7 +5529,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
if (!stateRBOObject) {
MGLOG_E("State RBO object is null, cannot sync to backend.");
MGLOG_E_ONCE("State RBO object is null, cannot sync to backend.");
return;
}
+9 -9
View File
@@ -252,7 +252,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
g_resolvedTier =
ResolveTier(g_GLESCapabilities, g_GLESFuncs, MG_Config::Features.EsprytMultiDrawMode,
&g_tierResolution);
MGLOG_I("DirectGLES multi-draw: %s", g_tierResolution.c_str());
MGLOG_D("DirectGLES multi-draw: %s", g_tierResolution.c_str());
}
// Which tiers have already announced themselves, one bit per GLESMultiDrawMode.
@@ -267,7 +267,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
const Uint32 bit = 1u << static_cast<Uint32>(tier);
if (g_announcedTiers & bit) return;
g_announcedTiers |= bit;
MGLOG_I("DirectGLES multi-draw: first batch executed via tier \"%s\"", TierName(tier));
MGLOG_D("DirectGLES multi-draw: first batch executed via tier \"%s\"", TierName(tier));
}
// The tier this particular batch can actually take. A tier is demoted here when
@@ -490,7 +490,7 @@ namespace MobileGL::MG_Backend::DirectGLES::MultiDrawImpl {
const Uint8* source = ResolveSubDrawIndices(indexBuffer, indexBufferBytes, indexBufferSize, indices[i],
subDrawCount, indexSize);
if (!source) {
MGLOG_E("DirectGLES multi-draw (drawelements tier): sub-draw %d reads outside the bound index "
MGLOG_E_ONCE("DirectGLES multi-draw (drawelements tier): sub-draw %d reads outside the bound index "
"buffer; skipping the batch",
i);
return false;
@@ -596,7 +596,7 @@ void main() {
const GLuint shader = g_GLESFuncs.glCreateShader(GL_COMPUTE_SHADER);
if (shader == 0) {
MGLOG_E("DirectGLES multi-draw (compute tier): glCreateShader(GL_COMPUTE_SHADER) failed");
MGLOG_E_ONCE("DirectGLES multi-draw (compute tier): glCreateShader(GL_COMPUTE_SHADER) failed");
return false;
}
const char* source = kFlattenComputeSource;
@@ -607,14 +607,14 @@ void main() {
if (status != GL_TRUE) {
char log[1024] = {};
g_GLESFuncs.glGetShaderInfoLog(shader, sizeof(log) - 1, nullptr, log);
MGLOG_E("DirectGLES multi-draw (compute tier): index-flattening shader failed to compile: %s", log);
MGLOG_E_ONCE("DirectGLES multi-draw (compute tier): index-flattening shader failed to compile: %s", log);
g_GLESFuncs.glDeleteShader(shader);
return false;
}
const GLuint program = g_GLESFuncs.glCreateProgram();
if (program == 0) {
MGLOG_E("DirectGLES multi-draw (compute tier): glCreateProgram failed");
MGLOG_E_ONCE("DirectGLES multi-draw (compute tier): glCreateProgram failed");
g_GLESFuncs.glDeleteShader(shader);
return false;
}
@@ -625,7 +625,7 @@ void main() {
if (status != GL_TRUE) {
char log[1024] = {};
g_GLESFuncs.glGetProgramInfoLog(program, sizeof(log) - 1, nullptr, log);
MGLOG_E("DirectGLES multi-draw (compute tier): index-flattening program failed to link: %s", log);
MGLOG_E_ONCE("DirectGLES multi-draw (compute tier): index-flattening program failed to link: %s", log);
g_GLESFuncs.glDeleteProgram(program);
return false;
}
@@ -635,7 +635,7 @@ void main() {
g_uDrawCount = g_GLESFuncs.glGetUniformLocation(program, "uDrawCount");
g_uTotalIndices = g_GLESFuncs.glGetUniformLocation(program, "uTotalIndices");
g_computeProgramFailed = false;
MGLOG_I("DirectGLES multi-draw: index-flattening compute program ready (id %u)", program);
MGLOG_D("DirectGLES multi-draw: index-flattening compute program ready (id %u)", program);
return true;
}
@@ -920,7 +920,7 @@ void main() {
feedBaseVertex);
}
if (!drawn) {
MGLOG_E("DirectGLES multi-draw: no usable tier for a %d sub-draw batch (mode 0x%x, type 0x%x); "
MGLOG_E_ONCE("DirectGLES multi-draw: no usable tier for a %d sub-draw batch (mode 0x%x, type 0x%x); "
"the batch was dropped",
drawcount, mode, type);
}
+2 -2
View File
@@ -1176,7 +1176,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
for (GLenum err = g_GLESFuncs.glGetError(); err != GL_NO_ERROR; err = g_GLESFuncs.glGetError()) {
MGLOG_E("-> GLES Error: %s", MG_Util::ConvertGLEnumToString(err).c_str());
MGLOG_D("-> GLES Error: %s", MG_Util::ConvertGLEnumToString(err).c_str());
}
}
@@ -1642,7 +1642,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
static_cast<SizeT>(sliceCount - 1) * dstImageStride +
static_cast<SizeT>(sliceHeight - 1) * dstRowStride + dstRowBytes;
if (requiredSize > pixelPackBufferObject->GetSize()) {
MGLOG_E("Readback conversion: pixel pack buffer is too small");
MGLOG_E_ONCE("Readback conversion: pixel pack buffer is too small");
return true;
}
}
@@ -269,14 +269,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
drawBuffer->SyncPersistentMappedRange();
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
if (drawBuffer->MappedData() == nullptr || commandOffset + requiredBytes > drawBuffer->GetSize()) {
MGLOG_E("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
MGLOG_E_ONCE("%s skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range", label);
return nullptr;
}
return drawBuffer->MappedData() + commandOffset;
}
if (!indirect) {
MGLOG_E("%s skipped: indirect pointer is null", label);
MGLOG_E_ONCE("%s skipped: indirect pointer is null", label);
return nullptr;
}
@@ -398,7 +398,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
stride = sizeof(DrawArraysIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
MGLOG_E("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawArraysIndirect skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawArraysIndirectCommand));
return;
}
@@ -446,20 +446,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
stride = sizeof(DrawArraysIndirectCommand);
}
if (stride < static_cast<GLsizei>(sizeof(DrawArraysIndirectCommand))) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu",
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: stride %d is smaller than command size %zu",
stride, sizeof(DrawArraysIndirectCommand));
return;
}
auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
if (!parameterBuffer || drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
return;
}
parameterBuffer->SyncPersistentMappedRange();
if (parameterBuffer->MappedData() == nullptr) {
MGLOG_E("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read parameter buffer");
MGLOG_E_ONCE("MultiDrawArraysIndirectCount skipped: CPU fallback cannot read parameter buffer");
return;
}
@@ -513,7 +513,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
MGLOG_E_ONCE("DrawElementsIndirect skipped: unsupported index type 0x%x", type);
return;
}
@@ -1009,7 +1009,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// shift - the hardware divide was the hottest instruction of this loop.
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
if (indexSize == 0) {
MGLOG_E("MultiDrawElements skipped: unsupported index type 0x%x", type);
MGLOG_E_ONCE("MultiDrawElements skipped: unsupported index type 0x%x", type);
return;
}
const Uint32 indexSizeShift = static_cast<Uint32>(std::countr_zero(indexSize));
@@ -205,7 +205,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// commands away. The device is gone on that path anyway - stay silent-safe
// rather than trade a lost device for a barrier into a closed buffer.
if (frame.hasCommandBufferRecorded) {
MGLOG_E("TransitionToPresent: command buffer already closed; skipping the present barrier");
MGLOG_E_ONCE("TransitionToPresent: command buffer already closed; skipping the present barrier");
return false;
}
@@ -259,7 +259,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// is the correct price for a broken pipeline and is bounded by the draw itself being
// skipped.
if (pipeline == VK_NULL_HANDLE) {
MGLOG_I("PipelineFactory::GetOrCreatePipeline: creation failed for hash=0x%llx "
// Unlatched, like the CreatePipeline report it accompanies: a pipeline MobileGL
// assembled and the driver refused is a broken invariant, not an expected failure,
// so it stays loud for as long as it is reachable. Raised from MGLOG_I once the
// Log.h ordering fix made MGLOG_E live in INFO builds.
MGLOG_E("PipelineFactory::GetOrCreatePipeline: creation failed for hash=0x%llx "
"programHash=0x%llx; not caching the failure",
static_cast<unsigned long long>(hash),
static_cast<unsigned long long>(payload.programHash));
@@ -506,7 +510,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static Bool s_warnedHalfTessellatedPipeline = false;
if (!s_warnedHalfTessellatedPipeline) {
s_warnedHalfTessellatedPipeline = true;
MGLOG_E("PipelineFactory::CreatePipeline: refusing a pipeline with %s tessellation stage and "
MGLOG_E_ONCE("PipelineFactory::CreatePipeline: refusing a pipeline with %s tessellation stage and "
"no %s stage (VUID-VkGraphicsPipelineCreateInfo-pStages-00730). programHash=0x%llx "
"patchControlPoints=%u. Its draws are skipped; logged once.",
hasTessEval ? "an evaluation" : "a control",
@@ -537,6 +541,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkPipeline pipeline = VK_NULL_HANDLE;
const VkResult result = vkCreateGraphicsPipelines(m_device, m_pipelineCache, 1, &gpi, nullptr, &pipeline);
// Loud, at MGLOG_F, and deliberately NOT latched. vkCreateGraphicsPipelines refusing a
// pipeline MobileGL assembled is a should-never-happen state, and the driver's own
// answer is VK_ERROR_UNKNOWN - no information at all - so this dump is the entire
// diagnosis. It is not an expected failure mode, so the one-shot rule that quiets W/E
// does not apply: while this is reachable it should keep saying so on every draw.
// GetOrCreatePipeline deliberately does not cache the failure, which is what makes that
// repetition happen; if the repetition ever needs to stop, fix the pipeline, not the log.
if (result != VK_SUCCESS) {
MGLOG_F("PipelineFactory::CreatePipeline failed: result=%s (%d) programHash=0x%llx vertexInputHash=0x%llx stageCount=%u topology=%s(%d) colorAttachmentCount=%u samples=%s(%d) subpass=%u",
VkResultToString(result),
@@ -569,8 +580,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.vertexInputState->vertexAttributeDescriptionCount);
// The driver's own answer is VK_ERROR_UNKNOWN, i.e. no information at all, so the only
// way to work out WHICH shader it choked on (the open sampler-array-in-struct
// investigation) is to name the modules. MGLOG_I, not _D/_E: this must survive in the
// INFO-level builds that CTS actually runs against.
// investigation) is to name the modules. MGLOG_I, not _D: this is part of a
// should-never-happen report and must survive in the INFO-level builds that CTS
// actually runs against, alongside the MGLOG_F lines above.
if (payload.stageSpirvDigests) {
for (SizeT i = 0; i < payload.stageSpirvDigests->size(); ++i) {
const auto& digest = (*payload.stageSpirvDigests)[i];
@@ -376,12 +376,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
spv_diagnostic diagnostic = nullptr;
const spv_result_t result = spvValidateWithOptions(context, options, &binary, &diagnostic);
if (result != SPV_SUCCESS) {
// MGLOG_I, not E: at the INFO compile level of the CI/test lanes that arm
// the validation switch, MGLOG_E is compiled out (Log.h orders
// DEBUG < WARN < ERROR < INFO) and the VUID would never reach a log. The
// latch is what a test harness asserts on.
// MGLOG_E, unlatched: reaching here already requires the validation switch to
// be armed, which bounds the volume, and each VUID names a different defect.
// (Parked at MGLOG_I until the Log.h level ordering was fixed, when E was
// compiled out of every INFO build.) The latch is what a test harness asserts on.
MG_Util::ShaderTranspiler::ShaderCompiler::NoteSpirvValidationFailure();
MGLOG_I(
MGLOG_E(
"ProgramFactory::ValidateTransformedSpirv: validation failed for stage=%d program=%u result=%d index=%zu msg=%s",
static_cast<Int>(shaderStage),
programExternalIndex,
@@ -1266,7 +1266,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
for (SizeT i = 1; i < group.offsets.size(); ++i) {
if (group.elementBytes == 0 ||
group.offsets[i] != group.offsets[i - 1] + group.elementBytes) {
MGLOG_I("XfbCaptureDecoratePass: block member %u of type %%%u is captured with a "
MGLOG_D("XfbCaptureDecoratePass: block member %u of type %%%u is captured with a "
"non-contiguous element set; the capture layout will differ from GL's",
key.second, key.first);
break;
@@ -1855,16 +1855,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// unification and the set->0 normalisation this function exists to do. A
// program with an image array plus any second descriptor got aliased
// bindings out of that, and a DEBUG build trapped on the same program.
// Which is also why the message below is MGLOG_I: MGLOG_E is compiled out
// of an INFO build, so a refusal that only said MGLOG_E said nothing at all
// in the builds that ship.
// The refusal below is MGLOG_E and per-program-compile, so it reports every
// program it declines. It spent time at MGLOG_I because the old level
// ordering compiled E out of the builds that ship.
const Bool arraySupportedForKind =
kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic ||
kind == ProgramFactory::DescriptorBindingKind::StorageBuffer ||
kind == ProgramFactory::DescriptorBindingKind::StorageImage ||
kind == ProgramFactory::DescriptorBindingKind::CombinedImageSampler;
if (binding->count != 1 && !arraySupportedForKind) {
MGLOG_I("ProgramFactory: descriptor arrays are unsupported for this descriptor "
MGLOG_E("ProgramFactory: descriptor arrays are unsupported for this descriptor "
"kind (name='%s' count=%u type=%d)",
binding->name ? binding->name : "<null>", binding->count,
static_cast<Int>(binding->descriptor_type));
@@ -2468,7 +2468,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// inert; a device whose binding cap is smaller than a shader's array is not a
// configuration MobileGL can serve at all. Needs a >maxBindings-element array to
// reach (256 on desktop, ~16 on mobile).
MGLOG_I("ProgramFactory::ReflectLayout: %s array '%s' at binding %u has %u elements, past the %u "
MGLOG_D("ProgramFactory::ReflectLayout: %s array '%s' at binding %u has %u elements, past the %u "
"this device can describe - declining the program",
kindLabel, uniformName.c_str(), binding, count, maxBindings);
outDeclined = true;
@@ -2476,7 +2476,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
if (baseLocation < 0 ||
!program.UniformLocationsAliasSameUniform(baseLocation, baseLocation + static_cast<Int>(count - 1u))) {
MGLOG_I("ProgramFactory::ReflectLayout: %s array '%s' at binding %u spans %u descriptors but the "
MGLOG_D("ProgramFactory::ReflectLayout: %s array '%s' at binding %u spans %u descriptors but the "
"reflection reserved fewer uniform locations for it (base=%d) - a multi-dimensional array "
"is the usual cause, and MobileGL declines it rather than resolve elements onto a "
"neighbouring uniform",
@@ -2713,7 +2713,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// a Uint16 on the way, where 65536 would silently become 0.
const Uint32 storageArrayCount = std::max<Uint32>(1u, sampler->count);
if (storageArrayCount > m_maxBindings) {
MGLOG_I("ProgramFactory::ReflectLayout: storage block array '%s' at binding %u has %u "
MGLOG_D("ProgramFactory::ReflectLayout: storage block array '%s' at binding %u has %u "
"elements, past the %u this device can describe - declining the program",
uniformName.c_str(), binding, storageArrayCount, m_maxBindings);
entry.declinedDescriptors = true;
@@ -2736,7 +2736,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// so at a level that survives a release build, because dropping the binding
// leaves the shader reading a descriptor the layout never declared.
if (sampler->count > 1) {
MGLOG_I("ProgramFactory::ReflectLayout: declining '%s' at binding %u - a %u-element "
MGLOG_E("ProgramFactory::ReflectLayout: declining '%s' at binding %u - a %u-element "
"descriptor array with no frontend uniform location (a multi-dimensional array "
"of samplers or images is the known cause)",
uniformName.c_str(), binding, sampler->count);
@@ -3201,7 +3201,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// "the layout and the shader disagree", so route it through that. Set AFTER ReflectLayout,
// which clears the flag.
if (!remapOk) {
MGLOG_I("ProgramFactory::GetOrCreateProgram: declining program %u - its descriptor bindings could not "
MGLOG_E("ProgramFactory::GetOrCreateProgram: declining program %u - its descriptor bindings could not "
"be remapped, so the layout does not describe what the shader reads",
program.GetExternalIndex());
entry.declinedDescriptors = true;
@@ -157,7 +157,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_I("Got %d surface formats:", swapchainCapabilities.surfaceFormats.size());
for (const auto& sf : swapchainCapabilities.surfaceFormats) {
MGLOG_I(" [%s, %s]", string_VkFormat(sf.format), string_VkColorSpaceKHR(sf.colorSpace));
MGLOG_D(" [%s, %s]", string_VkFormat(sf.format), string_VkColorSpaceKHR(sf.colorSpace));
}
const auto pickedSurfaceFormat = ChooseSwapchainSurfaceFormat(swapchainCapabilities.surfaceFormats);
@@ -166,7 +166,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_I("Got %d present modes:", swapchainCapabilities.presentModes.size());
for (const auto& pm : swapchainCapabilities.presentModes) {
MGLOG_I(" %s", string_VkPresentModeKHR(pm));
MGLOG_D(" %s", string_VkPresentModeKHR(pm));
}
const auto presentMode = ChooseSwapchainPresentMode(swapchainCapabilities.presentModes);
@@ -157,7 +157,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDescriptorPool initialPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(m_setsPerFrame, initialPool)) {
MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame descriptor pool %u",
MGLOG_E_ONCE("UniformDescriptorBinder::Initialize failed: cannot create frame descriptor pool %u",
frameIndex);
Shutdown();
return false;
@@ -345,13 +345,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
fallbackHolder = GetFallbackTexture(preferredTarget);
texture = fallbackHolder.get();
if (texture == nullptr) {
MGLOG_E("ResolveSamplerDescriptor: no fallback texture available for binding=%u ('%s') "
MGLOG_E_ONCE("ResolveSamplerDescriptor: no fallback texture available for binding=%u ('%s') "
"location=%d unit=%d target=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), location, unit,
static_cast<Int>(preferredTarget));
return false;
}
MGLOG_W(
MGLOG_W_ONCE(
"ResolveSamplerDescriptor: using fallback texture for unbound sampler binding=%u ('%s') location=%d unit=%d target=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), location, unit,
static_cast<Int>(preferredTarget));
@@ -360,7 +360,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const MG_State::GLState::SamplerObject* samplerToUse =
samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get();
if (samplerToUse == nullptr) {
MGLOG_E(
MGLOG_E_ONCE(
"ResolveSamplerDescriptor: sampler binding %u ('%s') has no sampler object (textureId=%d location=%d unit=%d)",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(), location,
unit);
@@ -368,7 +368,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
VkTextureManager::TextureResource* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
if (resource == nullptr) {
MGLOG_E(
MGLOG_E_ONCE(
"ResolveSamplerDescriptor: sampler binding %u ('%s') failed to create/sync texture resource (textureId=%d target=%d location=%d unit=%d)",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()), location, unit);
@@ -380,7 +380,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Int attachmentLevel = 0;
if (drawFbo &&
FindFramebufferAttachmentForTexture(*drawFbo, *texture, attachmentType, attachmentLevel)) {
MGLOG_W("ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
MGLOG_W_ONCE("ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
"for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, "
"trackedLayout=%d)",
texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(),
@@ -390,7 +390,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Bool readyForSampling = m_textureManager->TransitionTextureForSampling(commandBuffer, *texture);
if (!readyForSampling) {
MGLOG_E("ResolveSamplerDescriptor: failed to transition textureId=%d for sampler binding=%u",
MGLOG_E_ONCE("ResolveSamplerDescriptor: failed to transition textureId=%d for sampler binding=%u",
texture->GetExternalIndex(), binding);
return false;
}
@@ -432,7 +432,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
}
if (sampledViewFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("ResolveSamplerDescriptor: no compatible sampled view for binding=%u ('%s') "
MGLOG_E_ONCE("ResolveSamplerDescriptor: no compatible sampled view for binding=%u ('%s') "
"textureId=%d imageFormat=%d numericDomain=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(resource->format), static_cast<Int>(numericDomain));
@@ -445,7 +445,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
? resource->sampledView
: m_textureManager->GetOrCreateSampledImageView(*texture, sampledViewFormat);
if (sampledImageView == VK_NULL_HANDLE) {
MGLOG_E("ResolveSamplerDescriptor: failed to resolve sampled view for binding=%u ('%s') "
MGLOG_E_ONCE("ResolveSamplerDescriptor: failed to resolve sampled view for binding=%u ('%s') "
"textureId=%d imageFormat=%d viewFormat=%d numericDomain=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(resource->format), static_cast<Int>(sampledViewFormat),
@@ -671,14 +671,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
SharedPtr<MG_State::GLState::ITextureObject> texture;
if (!ResolveSamplerTexture(program, programObj, binding, texture) || texture == nullptr) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') is unbound", binding,
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') is unbound", binding,
programObj.samplerNameByBinding[binding].c_str());
return false;
}
if (texture->GetStorageType() != TextureStorageType::Buffer ||
texture->GetTarget() != TextureTarget::TextureBuffer) {
MGLOG_E(
MGLOG_E_ONCE(
"ResolveTexelBufferDescriptor: binding %u ('%s') expected texture buffer, got textureId=%u target=%d storage=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()), static_cast<Int>(texture->GetStorageType()));
@@ -688,14 +688,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto* textureBuffer = static_cast<MG_State::GLState::TextureObjectBuffer*>(texture.get());
const auto& bufferObject = textureBuffer->GetBufferBindingSlot().GetBoundObject();
if (bufferObject == nullptr) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') has no GL buffer bound",
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') has no GL buffer bound",
binding, programObj.samplerNameByBinding[binding].c_str());
return false;
}
BufferSlice slice{};
if (!m_bufferManager->AcquireResidentSlice(BufferKind::TextureBuffer, bufferObject, slice) || !slice.IsValid()) {
MGLOG_E("ResolveTexelBufferDescriptor: failed to sync GL buffer %u for texture buffer %u",
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: failed to sync GL buffer %u for texture buffer %u",
bufferObject->GetExternalIndex(), texture->GetExternalIndex());
return false;
}
@@ -703,7 +703,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto internalFormat = textureBuffer->GetFormat();
const VkFormat vkFormat = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
if (vkFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("ResolveTexelBufferDescriptor: unsupported texture buffer internal format %d",
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: unsupported texture buffer internal format %d",
static_cast<Int>(internalFormat));
return false;
}
@@ -719,7 +719,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewRange = (viewRange / texelSize) * texelSize;
}
if (viewRange == 0) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer %u has empty view range", texture->GetExternalIndex());
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: texture buffer %u has empty view range", texture->GetExternalIndex());
return false;
}
@@ -733,7 +733,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferView bufferView = VK_NULL_HANDLE;
const VkResult result = vkCreateBufferView(m_device, &viewInfo, nullptr, &bufferView);
if (result != VK_SUCCESS || bufferView == VK_NULL_HANDLE) {
MGLOG_E("ResolveTexelBufferDescriptor: vkCreateBufferView failed result=%d format=%d range=%zu",
MGLOG_E_ONCE("ResolveTexelBufferDescriptor: vkCreateBufferView failed result=%d format=%d range=%zu",
result, static_cast<Int>(vkFormat), static_cast<SizeT>(viewRange));
return false;
}
@@ -769,13 +769,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Int location = programObj.samplerUniformLocationByBinding[binding];
if (location < 0) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: binding %u ('%s') has no uniform location", binding,
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: binding %u ('%s') has no uniform location", binding,
programObj.samplerNameByBinding[binding].c_str());
return false;
}
const Int imageUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (imageUnit < 0 || imageUnit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: image unit %d out of range for binding %u", imageUnit,
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: image unit %d out of range for binding %u", imageUnit,
binding);
return false;
}
@@ -783,13 +783,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(imageUnit);
const auto& texture = imageBinding.Texture;
if (texture == nullptr) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: image unit %d is unbound for binding %u", imageUnit,
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: image unit %d is unbound for binding %u", imageUnit,
binding);
return false;
}
if (texture->GetStorageType() != TextureStorageType::Buffer ||
texture->GetTarget() != TextureTarget::TextureBuffer) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: binding %u ('%s') expected a texture buffer on image "
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: binding %u ('%s') expected a texture buffer on image "
"unit %d, got textureId=%u target=%d storage=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), imageUnit,
texture->GetExternalIndex(), static_cast<Int>(texture->GetTarget()),
@@ -800,7 +800,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto* textureBuffer = static_cast<MG_State::GLState::TextureObjectBuffer*>(texture.get());
const auto& bufferObject = textureBuffer->GetBufferBindingSlot().GetBoundObject();
if (bufferObject == nullptr) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: texture buffer on image unit %d has no GL buffer bound",
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: texture buffer on image unit %d has no GL buffer bound",
imageUnit);
return false;
}
@@ -819,7 +819,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
BufferSlice slice{};
if (!m_bufferManager->AcquireResidentSlice(BufferKind::TextureBuffer, bufferObject, slice) ||
!slice.IsValid()) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: failed to sync GL buffer %u for texture buffer %u",
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: failed to sync GL buffer %u for texture buffer %u",
bufferObject->GetExternalIndex(), texture->GetExternalIndex());
return false;
}
@@ -842,7 +842,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkFormat = resourceFormat;
}
if (vkFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: unsupported image buffer format (internal=%d bind=0x%x)",
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: unsupported image buffer format (internal=%d bind=0x%x)",
static_cast<Int>(internalFormat), imageBinding.Format);
return false;
}
@@ -862,7 +862,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewRange = (viewRange / texelSize) * texelSize;
}
if (viewRange == 0) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: texture buffer %u has empty view range",
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: texture buffer %u has empty view range",
texture->GetExternalIndex());
return false;
}
@@ -877,7 +877,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferView bufferView = VK_NULL_HANDLE;
const VkResult result = vkCreateBufferView(m_device, &viewInfo, nullptr, &bufferView);
if (result != VK_SUCCESS || bufferView == VK_NULL_HANDLE) {
MGLOG_E("ResolveStorageTexelBufferDescriptor: vkCreateBufferView failed result=%d format=%d range=%zu",
MGLOG_E_ONCE("ResolveStorageTexelBufferDescriptor: vkCreateBufferView failed result=%d format=%d range=%zu",
result, static_cast<Int>(vkFormat), static_cast<SizeT>(viewRange));
return false;
}
@@ -914,7 +914,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, frontendBinding);
const auto& bufferObject = bindingPoint.GetBoundObject();
if (bufferObject == nullptr) {
MGLOG_E("ResolveStorageBufferDescriptor: no SSBO bound at frontend binding %u for block '%s'",
MGLOG_E_ONCE("ResolveStorageBufferDescriptor: no SSBO bound at frontend binding %u for block '%s'",
frontendBinding, programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
@@ -929,7 +929,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
BufferSlice slice{};
if (!m_bufferManager->AcquireResidentSlice(BufferKind::ShaderStorage, bufferObject, slice) || !slice.IsValid()) {
MGLOG_E("ResolveStorageBufferDescriptor: failed to sync GL buffer %u for block '%s'",
MGLOG_E_ONCE("ResolveStorageBufferDescriptor: failed to sync GL buffer %u for block '%s'",
bufferObject->GetExternalIndex(), programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
@@ -943,7 +943,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
rangeEnd = bufferSize;
}
if (rangeEnd <= rangeStart) {
MGLOG_E("ResolveStorageBufferDescriptor: empty SSBO range for block '%s'",
MGLOG_E_ONCE("ResolveStorageBufferDescriptor: empty SSBO range for block '%s'",
programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
@@ -967,7 +967,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Int baseLocation = programObj.samplerUniformLocationByBinding[binding];
if (baseLocation < 0) {
MGLOG_E("ResolveStorageImageDescriptor: storage image binding %u has no uniform location", binding);
MGLOG_E_ONCE("ResolveStorageImageDescriptor: storage image binding %u has no uniform location", binding);
return false;
}
// Per ELEMENT, and this is where an image array differs from a storage-block array: GL
@@ -979,26 +979,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// uniform.
const Int location = baseLocation + static_cast<Int>(element);
if (!program.UniformLocationsAliasSameUniform(baseLocation, location)) {
MGLOG_E("ResolveStorageImageDescriptor: binding %u element %u is past the end of its image array",
MGLOG_E_ONCE("ResolveStorageImageDescriptor: binding %u element %u is past the end of its image array",
binding, element);
return false;
}
const Int imageUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (imageUnit < 0 || imageUnit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MGLOG_E("ResolveStorageImageDescriptor: image unit %d out of range for binding %u",
MGLOG_E_ONCE("ResolveStorageImageDescriptor: image unit %d out of range for binding %u",
imageUnit, binding);
return false;
}
auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(imageUnit);
if (imageBinding.Texture == nullptr) {
MGLOG_E("ResolveStorageImageDescriptor: image unit %d is unbound for binding %u", imageUnit, binding);
MGLOG_E_ONCE("ResolveStorageImageDescriptor: image unit %d is unbound for binding %u", imageUnit, binding);
return false;
}
const Bool ready = m_textureManager->TransitionTextureForStorageImage(commandBuffer, *imageBinding.Texture);
if (!ready) {
MGLOG_E("ResolveStorageImageDescriptor: failed to transition textureId=%d for image unit %d",
MGLOG_E_ONCE("ResolveStorageImageDescriptor: failed to transition textureId=%d for image unit %d",
imageBinding.Texture->GetExternalIndex(), imageUnit);
return false;
}
@@ -1018,7 +1018,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkFormat viewFormat = ResolveStorageImageViewFormat(
reflectedFormat, imageBinding.Format, resource->format, useBindingFormat);
if (viewFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("ResolveStorageImageDescriptor: unsupported glBindImageTexture format=0x%x "
MGLOG_E_ONCE("ResolveStorageImageDescriptor: unsupported glBindImageTexture format=0x%x "
"for binding=%u imageUnit=%d textureId=%d bindingPolicy=%s",
imageBinding.Format, binding, imageUnit, imageBinding.Texture->GetExternalIndex(),
useBindingFormat ? "true" : "false");
@@ -1027,7 +1027,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkImageView view = m_textureManager->GetOrCreateStorageImageView(
*imageBinding.Texture, mipLevel, viewFormat, imageBinding.Layered != GL_FALSE, imageBinding.Layer);
if (view == VK_NULL_HANDLE) {
MGLOG_E("ResolveStorageImageDescriptor: failed to resolve storage view textureId=%d mip=%u "
MGLOG_E_ONCE("ResolveStorageImageDescriptor: failed to resolve storage view textureId=%d mip=%u "
"bindingFormat=0x%x imageFormat=%d reflectedFormat=%d selectedFormat=%d bindingPolicy=%s",
imageBinding.Texture->GetExternalIndex(), mipLevel, imageBinding.Format,
static_cast<Int>(resource->format), static_cast<Int>(reflectedFormat),
@@ -1048,7 +1048,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Report that there is no fallback and let the caller decline the draw - aborting the
// process over an unbound sampler is never the right answer.
if (target != TextureTarget::Texture2D && target != TextureTarget::TextureRectangle) {
MGLOG_E("UniformManager::GetFallbackTexture: no fallback exists for target=%d",
MGLOG_E_ONCE("UniformManager::GetFallbackTexture: no fallback exists for target=%d",
static_cast<Int>(target));
return nullptr;
}
@@ -1224,13 +1224,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
continue;
}
if (binding >= programObj.samplerUniformLocationByBinding.size()) {
MGLOG_E("CollectStorageImageTextures: binding %u has no uniform-location mapping", binding);
MGLOG_E_ONCE("CollectStorageImageTextures: binding %u has no uniform-location mapping", binding);
return false;
}
const Int baseLocation = programObj.samplerUniformLocationByBinding[binding];
if (baseLocation < 0) {
MGLOG_E("CollectStorageImageTextures: binding %u has no image uniform location", binding);
MGLOG_E_ONCE("CollectStorageImageTextures: binding %u has no image uniform location", binding);
return false;
}
// Per ELEMENT, for the same reason the sampled walk above is: an image ARRAY is one
@@ -1242,20 +1242,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
for (Uint32 element = 0; element < descriptorCount; ++element) {
const Int location = ResolveDescriptorElementLocation(program, baseLocation, element);
if (location < 0) {
MGLOG_E("CollectStorageImageTextures: binding %u element %u is past the end of its image array",
MGLOG_E_ONCE("CollectStorageImageTextures: binding %u element %u is past the end of its image array",
binding, element);
return false;
}
const Int imageUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (imageUnit < 0 || imageUnit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MGLOG_E("CollectStorageImageTextures: image unit %d is invalid for binding %u element %u",
MGLOG_E_ONCE("CollectStorageImageTextures: image unit %d is invalid for binding %u element %u",
imageUnit, binding, element);
return false;
}
auto* texture = MG_State::pGLContext->GetImageTextureBinding(imageUnit).Texture.get();
if (texture == nullptr) {
MGLOG_E("CollectStorageImageTextures: image unit %d is unbound for binding %u element %u",
MGLOG_E_ONCE("CollectStorageImageTextures: image unit %d is unbound for binding %u element %u",
imageUnit, binding, element);
return false;
}
@@ -1406,7 +1406,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Uint64 descriptorCount64 =
static_cast<Uint64>(maxSets) * static_cast<Uint64>(std::min(m_maxBindings, kEstimatedBindingsPerSet));
if (descriptorCount64 > static_cast<Uint64>(std::numeric_limits<Uint32>::max())) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: descriptorCount overflow");
MGLOG_E_ONCE("UniformDescriptorBinder::CreateDescriptorPool failed: descriptorCount overflow");
return false;
}
@@ -1438,7 +1438,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &outPool);
if (result != VK_SUCCESS) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d",
MGLOG_E_ONCE("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d",
result);
return false;
}
@@ -1457,7 +1457,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDescriptorPool grownPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(grownMaxSets, grownPool)) {
MGLOG_E("UniformDescriptorBinder::GrowFrameDescriptorPool failed: cannot create grown pool (%u -> %u sets)",
MGLOG_E_ONCE("UniformDescriptorBinder::GrowFrameDescriptorPool failed: cannot create grown pool (%u -> %u sets)",
currentMaxSets, grownMaxSets);
return false;
}
@@ -1516,7 +1516,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet);
if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) {
if (!GrowFrameDescriptorPool(frame, frameIndex)) {
MGLOG_E("UniformDescriptorBinder::AcquireDescriptorSet failed: descriptor pool growth failed");
MGLOG_E_ONCE("UniformDescriptorBinder::AcquireDescriptorSet failed: descriptor pool growth failed");
return allocResult;
}
allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet);
@@ -1647,7 +1647,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
auto& frame = m_frames[frameIndex];
if (frame.descriptorPools.empty()) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pools are invalid");
MGLOG_E_ONCE("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pools are invalid");
return false;
}
if (frame.activeDescriptorPoolIndex >= frame.descriptorPools.size()) {
@@ -1784,7 +1784,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferView bufferView = VK_NULL_HANDLE;
if (!ResolveTexelBufferDescriptor(program, programObj, binding, frameIndex, bufferView) ||
bufferView == VK_NULL_HANDLE) {
MGLOG_E(
MGLOG_E_ONCE(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: texture buffer binding %u has no valid descriptor",
binding);
return false;
@@ -1803,7 +1803,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferView bufferView = VK_NULL_HANDLE;
if (!ResolveStorageTexelBufferDescriptor(program, programObj, binding, frameIndex, bufferView) ||
bufferView == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: image buffer binding %u "
MGLOG_E_ONCE("UniformDescriptorBinder::BindProgramUniformBuffers failed: image buffer binding %u "
"has no valid descriptor",
binding);
return false;
@@ -1823,7 +1823,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
for (Uint32 element = 0; element < descriptorCount; ++element) {
VkDescriptorBufferInfo bufferInfo{};
if (!ResolveStorageBufferDescriptor(program, programObj, binding, element, bufferInfo)) {
MGLOG_E(
MGLOG_E_ONCE(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: storage buffer binding %u "
"element %u has no valid descriptor",
binding, element);
@@ -1850,7 +1850,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDescriptorImageInfo imageInfo{};
if (!ResolveStorageImageDescriptor(commandBuffer, program, programObj, binding, element,
imageInfo)) {
MGLOG_E(
MGLOG_E_ONCE(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: storage image binding %u "
"element %u has no valid descriptor",
binding, element);
@@ -1892,14 +1892,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
imageInfo, samplerDescriptorsUnchangedHint);
}
if (!hasImage) {
MGLOG_E(
MGLOG_E_ONCE(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u element %u "
"has no valid texture descriptor",
binding, element);
return false;
}
if (imageInfo.sampler == VK_NULL_HANDLE || imageInfo.imageView == VK_NULL_HANDLE) {
MGLOG_E(
MGLOG_E_ONCE(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u element %u "
"has null sampler or imageView",
binding, element);
@@ -1973,7 +1973,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} else {
VkResult allocResult = AcquireDescriptorSet(frameIndex, programObj, descriptorSet);
if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor set acquire returned %d",
MGLOG_E_ONCE("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor set acquire returned %d",
allocResult);
return false;
}
@@ -110,7 +110,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkFormat sourceVkFormat =
ToVkVertexFormat(attr.Type, attr.Size, attr.Normalized, attr.IsInteger, attr.IsBgra, attr.IsLong);
if (sourceVkFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("Unsupported vertex attribute layout (location=%u, type=%s, size=%d): the array is "
MGLOG_E_ONCE("Unsupported vertex attribute layout (location=%u, type=%s, size=%d): the array is "
"enabled but cannot be mapped to a VkFormat",
location, MG_Util::ConvertDataTypeToString(attr.Type).c_str(), attr.Size);
unsupportedAttribMask |= (1u << location);
@@ -125,7 +125,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (fallbackFormat != VK_FORMAT_UNDEFINED && SupportsVertexBufferFormat(fallbackFormat)) {
vkFormat = fallbackFormat;
conversion = VertexStreamConversion::ScaledIntegerToFloat32;
MGLOG_W("Vertex attribute location=%u format=%d lacks "
MGLOG_W_ONCE("Vertex attribute location=%u format=%d lacks "
"VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT; using float32 stream format=%d "
"(type=%s size=%d normalized=%s integer=%s)",
location, static_cast<Int>(sourceVkFormat), static_cast<Int>(vkFormat),
@@ -135,7 +135,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
if (conversion == VertexStreamConversion::None) {
MGLOG_E("Unsupported Vulkan vertex format (location=%u, format=%d, type=%s, size=%d): "
MGLOG_E_ONCE("Unsupported Vulkan vertex format (location=%u, format=%d, type=%s, size=%d): "
"VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT is unavailable and no semantic fallback exists",
location, static_cast<Int>(sourceVkFormat),
MG_Util::ConvertDataTypeToString(attr.Type).c_str(), attr.Size);
@@ -146,7 +146,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const SizeT attribByteSize = GetAttributeByteSize(attr.Type, attr.Size, attr.IsBgra);
if (attribByteSize == 0) {
MGLOG_E("Vertex attribute with unknown component size (location=%u, type=%s): the array is "
MGLOG_E_ONCE("Vertex attribute with unknown component size (location=%u, type=%s): the array is "
"enabled but cannot be sized",
location, MG_Util::ConvertDataTypeToString(attr.Type).c_str());
unsupportedAttribMask |= (1u << location);
@@ -175,7 +175,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// unless VK_EXT_legacy_vertex_attributes is available, so deinterleave this one
// attribute into a tightly packed transient stream without changing its format.
conversion = VertexStreamConversion::Repack;
MGLOG_W("Vertex attribute location=%u uses Vulkan-incompatible alignment "
MGLOG_W_ONCE("Vertex attribute location=%u uses Vulkan-incompatible alignment "
"(offset=%zu stride=%u required=%zu); using a tightly packed stream",
location, attr.Offset, sourceStride, requiredAlignment);
}
@@ -302,7 +302,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.requiredFlags = requiredFlags,
});
if (!created || resource.buffer.Map() == nullptr) {
MGLOG_E("VkBufferManager::CreateResidentStorage failed (size=%llu)",
MGLOG_E_ONCE("VkBufferManager::CreateResidentStorage failed (size=%llu)",
static_cast<unsigned long long>(size));
resource.buffer.Destroy();
resource.storageSize = 0;
@@ -324,7 +324,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false;
}
if (!resource.buffer.Upload(bufferObject.MappedData(), size, 0)) {
MGLOG_E("VkBufferManager::SwapStorageAndUploadAll: upload failed");
MGLOG_E_ONCE("VkBufferManager::SwapStorageAndUploadAll: upload failed");
resource.pendingFullUpload = true;
return false;
}
@@ -409,7 +409,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
if (!resource->buffer.Upload(bufferObject.MappedData(), size, 0)) {
MGLOG_E("VkBufferManager::OnRespecify: in-place upload failed");
MGLOG_E_ONCE("VkBufferManager::OnRespecify: in-place upload failed");
resource->pendingFullUpload = true;
}
}
@@ -434,7 +434,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!IsResourceBusy(*resource)) {
if (!resource->buffer.Upload(bufferObject.MappedData() + offset,
static_cast<VkDeviceSize>(size), static_cast<VkDeviceSize>(offset))) {
MGLOG_E("VkBufferManager::OnSubData: host upload failed");
MGLOG_E_ONCE("VkBufferManager::OnSubData: host upload failed");
resource->pendingFullUpload = true;
}
return;
@@ -471,7 +471,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if ((appAccess & BufferMappingAccessBit::Unsynchronized) || !IsResourceBusy(*resource)) {
if (!resource->buffer.Upload(bufferObject.MappedData() + offset,
static_cast<VkDeviceSize>(size), static_cast<VkDeviceSize>(offset))) {
MGLOG_E("VkBufferManager::OnFlushMappedRange: host upload failed");
MGLOG_E_ONCE("VkBufferManager::OnFlushMappedRange: host upload failed");
resource->pendingFullUpload = true;
}
return;
@@ -563,7 +563,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkDeviceSize size = static_cast<VkDeviceSize>(bufferObject->GetSize());
if (size == 0) {
MGLOG_E("VkBufferManager::AcquireResidentSlice failed: buffer size is zero");
MGLOG_E_ONCE("VkBufferManager::AcquireResidentSlice failed: buffer size is zero");
return false;
}
@@ -585,7 +585,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false;
}
if (!resource->buffer.Upload(bufferObject->MappedData(), size, 0)) {
MGLOG_E("VkBufferManager::AcquireResidentSlice failed: initial upload failed");
MGLOG_E_ONCE("VkBufferManager::AcquireResidentSlice failed: initial upload failed");
resource->buffer.Destroy();
resource->storageSize = 0;
resource->usageFlags = 0;
@@ -620,7 +620,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkDeviceSize size = static_cast<VkDeviceSize>(bufferObject->GetSize());
if (size == 0) {
MGLOG_E("VkBufferManager::AcquireStreamedSlice failed: buffer size is zero");
MGLOG_E_ONCE("VkBufferManager::AcquireStreamedSlice failed: buffer size is zero");
return false;
}
@@ -76,7 +76,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkResult result =
vmaCreateBuffer(m_allocator, &bufferInfo, &allocationInfo, &m_buffer, &m_allocation, nullptr);
if (result != VK_SUCCESS) {
MGLOG_E("VkBufferObject::Create failed: vmaCreateBuffer returned %d", result);
MGLOG_E_ONCE("VkBufferObject::Create failed: vmaCreateBuffer returned %d", result);
m_allocator = nullptr;
m_buffer = VK_NULL_HANDLE;
m_allocation = nullptr;
@@ -108,7 +108,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &m_mappedData);
if (mapResult != VK_SUCCESS || m_mappedData == nullptr) {
MGLOG_E("VkBufferObject::Map failed: vmaMapMemory returned %d", mapResult);
MGLOG_E_ONCE("VkBufferObject::Map failed: vmaMapMemory returned %d", mapResult);
m_mappedData = nullptr;
return nullptr;
}
@@ -138,14 +138,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Bool wasMapped = IsMapped();
void* mapped = wasMapped ? m_mappedData : Map();
if (mapped == nullptr) {
MGLOG_E("VkBufferObject::Upload failed: unable to map buffer");
MGLOG_E_ONCE("VkBufferObject::Upload failed: unable to map buffer");
return false;
}
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
const VkResult flushResult = vmaFlushAllocation(m_allocator, m_allocation, offset, size);
if (flushResult != VK_SUCCESS) {
MGLOG_E("VkBufferObject::Upload failed: vmaFlushAllocation returned %d", flushResult);
MGLOG_E_ONCE("VkBufferObject::Upload failed: vmaFlushAllocation returned %d", flushResult);
if (!wasMapped) {
Unmap();
}
@@ -170,7 +170,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkResult result = vmaInvalidateAllocation(m_allocator, m_allocation, offset, resolvedSize);
if (result != VK_SUCCESS) {
MGLOG_E("VkBufferObject::Invalidate failed: vmaInvalidateAllocation returned %d", result);
MGLOG_E_ONCE("VkBufferObject::Invalidate failed: vmaInvalidateAllocation returned %d", result);
return false;
}
return true;
@@ -123,7 +123,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
if (!attachment.IsComplete()) {
MGLOG_W("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u has an incomplete texture attachment; using VK_ATTACHMENT_UNUSED",
MGLOG_W_ONCE("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u has an incomplete texture attachment; using VK_ATTACHMENT_UNUSED",
drawBufferIndex,
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
fbo.GetExternalIndex());
@@ -132,7 +132,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto* texture = attachment.GetTexture().get();
if (texture == nullptr) {
MGLOG_W("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u resolved to a null texture; using VK_ATTACHMENT_UNUSED",
MGLOG_W_ONCE("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u resolved to a null texture; using VK_ATTACHMENT_UNUSED",
drawBufferIndex,
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
fbo.GetExternalIndex());
@@ -311,7 +311,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
if (!TryResolveSampleCountFlagBits(renderbuffer->GetSamples(), sampleCount)) {
MGLOG_E("GetOrCreateRenderbufferResource: unsupported renderbuffer sample count %d for renderbuffer %u",
MGLOG_E_ONCE("GetOrCreateRenderbufferResource: unsupported renderbuffer sample count %d for renderbuffer %u",
renderbuffer->GetSamples(),
renderbuffer->GetExternalIndex());
return nullptr;
@@ -457,7 +457,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_physicalDevice, format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags,
&imageFormatProperties);
if (imageFormatResult != VK_SUCCESS || (imageFormatProperties.sampleCounts & sampleCount) == 0) {
MGLOG_E("GetOrCreateRenderbufferResource: unsupported renderbuffer format=%d samples=%d for renderbuffer %u",
MGLOG_E_ONCE("GetOrCreateRenderbufferResource: unsupported renderbuffer format=%d samples=%d for renderbuffer %u",
static_cast<Int>(format),
static_cast<Int>(sampleCount),
renderbuffer->GetExternalIndex());
@@ -929,7 +929,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto& renderbuffer = rbAtt.GetRenderbuffer();
auto* rbResource = GetOrCreateRenderbufferResource(renderbuffer);
if (rbResource == nullptr || (rbResource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) {
MGLOG_E("GetOrCreateRenderPass: draw buffer slot %u on FBO %u has an unsupported color "
MGLOG_E_ONCE("GetOrCreateRenderPass: draw buffer slot %u on FBO %u has an unsupported color "
"renderbuffer %u; using VK_ATTACHMENT_UNUSED",
i, fbo.GetExternalIndex(), renderbuffer->GetExternalIndex());
continue;
@@ -1105,7 +1105,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
adoptRenderPassSampleCount(attachmentSampleCount, "color", texture->GetExternalIndex());
if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
MGLOG_W("GetOrCreateRenderPass: color attachment textureId=%d starts with undefined layout and no clear; "
MGLOG_W_ONCE("GetOrCreateRenderPass: color attachment textureId=%d starts with undefined layout and no clear; "
"using LOAD_OP_DONT_CARE",
texture->GetExternalIndex());
desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
@@ -1161,7 +1161,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) &&
!sameDepthStencilAttachmentObject(depthAtt, stencilAtt);
if (hasDistinctDepthAndStencilAttachments) {
MGLOG_E("GetOrCreateRenderPass: separate depth/stencil attachments are not supported yet; using the depth attachment and ignoring the standalone stencil attachment for framebuffer %u",
MGLOG_E_ONCE("GetOrCreateRenderPass: separate depth/stencil attachments are not supported yet; using the depth attachment and ignoring the standalone stencil attachment for framebuffer %u",
fbo.GetExternalIndex());
}
if (selectedDepthStencilAttachment != nullptr) {
@@ -1223,7 +1223,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachmentDescription.initialLayout = loadInfo.initialLayout;
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) {
MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment id=%d starts with undefined layout "
MGLOG_W_ONCE("GetOrCreateRenderPass: depth/stencil attachment id=%d starts with undefined layout "
"and partial/no clear; using DONT_CARE for uncleared aspects",
depthAttachmentId);
}
@@ -975,13 +975,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return resource->sampledView;
}
if (!AreSampledImageViewFormatsCompatible(resource->format, format)) {
MGLOG_E("%s: incompatible sampled image view format=%d for textureId=%d imageFormat=%d",
MGLOG_E_ONCE("%s: incompatible sampled image view format=%d for textureId=%d imageFormat=%d",
__func__, static_cast<Int>(format), texture.GetExternalIndex(),
static_cast<Int>(resource->format));
return VK_NULL_HANDLE;
}
if ((resource->imageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) == 0) {
MGLOG_E("%s: textureId=%d needs mutable image format=%d for sampled view format=%d",
MGLOG_E_ONCE("%s: textureId=%d needs mutable image format=%d for sampled view format=%d",
__func__, texture.GetExternalIndex(), static_cast<Int>(resource->format),
static_cast<Int>(format));
return VK_NULL_HANDLE;
@@ -1001,7 +1001,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFormatProperties formatProperties{};
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &formatProperties);
if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0) {
MGLOG_E("%s: sampled image view format=%d lacks VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT "
MGLOG_E_ONCE("%s: sampled image view format=%d lacks VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT "
"for textureId=%d (available=0x%x)",
__func__, static_cast<Int>(format), texture.GetExternalIndex(),
static_cast<Uint32>(formatProperties.optimalTilingFeatures));
@@ -1015,7 +1015,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
resource->sampledBaseMipLevel, resource->sampledLevelCount, 0, resource->arrayLayers,
&sampledComponents, VK_IMAGE_USAGE_SAMPLED_BIT);
if (view == VK_NULL_HANDLE) {
MGLOG_E("%s: failed to create sampled image view textureId=%d imageFormat=%d viewFormat=%d",
MGLOG_E_ONCE("%s: failed to create sampled image view textureId=%d imageFormat=%d viewFormat=%d",
__func__, texture.GetExternalIndex(), static_cast<Int>(resource->format),
static_cast<Int>(format));
return VK_NULL_HANDLE;
@@ -1043,14 +1043,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
format = resource->format;
}
if (!AreStorageImageViewFormatsCompatible(resource->format, format)) {
MGLOG_E("%s: incompatible storage image view format=%d for textureId=%d imageFormat=%d",
MGLOG_E_ONCE("%s: incompatible storage image view format=%d for textureId=%d imageFormat=%d",
__func__, static_cast<Int>(format), texture.GetExternalIndex(),
static_cast<Int>(resource->format));
return VK_NULL_HANDLE;
}
if (format != resource->format &&
(resource->imageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) == 0) {
MGLOG_E("%s: textureId=%d needs mutable image format=%d for storage view format=%d",
MGLOG_E_ONCE("%s: textureId=%d needs mutable image format=%d for storage view format=%d",
__func__, texture.GetExternalIndex(), static_cast<Int>(resource->format),
static_cast<Int>(format));
return VK_NULL_HANDLE;
@@ -1070,7 +1070,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewType = VK_IMAGE_VIEW_TYPE_2D;
break;
case VK_IMAGE_VIEW_TYPE_3D:
MGLOG_E("%s: non-layered 3D storage views are unsupported for textureId=%d",
MGLOG_E_ONCE("%s: non-layered 3D storage views are unsupported for textureId=%d",
__func__, texture.GetExternalIndex());
return VK_NULL_HANDLE;
default:
@@ -1079,7 +1079,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (viewType != resource->viewType) {
if (layer < 0 || static_cast<Uint32>(layer) >= resource->arrayLayers) {
MGLOG_E("%s: storage image layer=%d is out of range for textureId=%d arrayLayers=%u",
MGLOG_E_ONCE("%s: storage image layer=%d is out of range for textureId=%d arrayLayers=%u",
__func__, layer, texture.GetExternalIndex(), resource->arrayLayers);
return VK_NULL_HANDLE;
}
@@ -1114,7 +1114,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFormatProperties formatProperties{};
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &formatProperties);
if ((formatProperties.optimalTilingFeatures & requiredFormatFeatures) != requiredFormatFeatures) {
MGLOG_E("%s: storage image view format=%d lacks required features=0x%x for textureId=%d "
MGLOG_E_ONCE("%s: storage image view format=%d lacks required features=0x%x for textureId=%d "
"(available=0x%x)",
__func__, static_cast<Int>(format), static_cast<Uint32>(requiredFormatFeatures),
texture.GetExternalIndex(), static_cast<Uint32>(formatProperties.optimalTilingFeatures));
@@ -1125,7 +1125,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
mipLevel, 1, baseArrayLayer, layerCount, nullptr,
VK_IMAGE_USAGE_STORAGE_BIT);
if (view == VK_NULL_HANDLE) {
MGLOG_E("%s: failed to create storage image view for textureId=%d mip=%u imageFormat=%d viewFormat=%d",
MGLOG_E_ONCE("%s: failed to create storage image view for textureId=%d mip=%u imageFormat=%d viewFormat=%d",
__func__, texture.GetExternalIndex(), mipLevel, static_cast<Int>(resource->format),
static_cast<Int>(format));
return VK_NULL_HANDLE;
@@ -1223,7 +1223,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
if (resource->layout == VK_IMAGE_LAYOUT_UNDEFINED) {
MGLOG_W("TransitionTextureForSampling: textureId=%d is still in VK_IMAGE_LAYOUT_UNDEFINED before sampling",
MGLOG_W_ONCE("TransitionTextureForSampling: textureId=%d is still in VK_IMAGE_LAYOUT_UNDEFINED before sampling",
texture.GetExternalIndex());
}
@@ -1574,7 +1574,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// targets this manager has no Vulkan image shape for yet (cube map arrays above all).
// Declining the sync leaves the texture unbacked - wrong, but recoverable - where an
// assertion would take the whole process down instead.
MGLOG_W("SyncTextureResource: unsupported uploadTarget=%s textureTarget=%s textureId=%d size=(%d,%d,%d) "
MGLOG_W_ONCE("SyncTextureResource: unsupported uploadTarget=%s textureTarget=%s textureId=%d size=(%d,%d,%d) "
"mipLevels=%u vkViewType=%d",
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
MG_Util::ConvertTextureTargetToString(texture.GetTarget()).c_str(), texture.GetExternalIndex(),
@@ -1803,7 +1803,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Losing reinterpreted views only degrades the formatless-image feature for
// this texture; failing creation would lose the texture entirely, so retry
// as a plain immutable-format image.
MGLOG_W("%s: mutable image format=%d is unsupported for textureId=%d; creating "
MGLOG_W_ONCE("%s: mutable image format=%d is unsupported for textureId=%d; creating "
"without VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT (format reinterpretation "
"will be unavailable for it)",
__func__, static_cast<Int>(format), texture.GetExternalIndex());
@@ -1821,7 +1821,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Losing 2D-array compatibility only costs per-slice framebuffer attachment for this
// format; failing creation would lose the texture entirely. Remembered so later syncs
// neither reprobe nor flag-mismatch against this image and recreate it.
MGLOG_W("%s: VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT is unsupported for format=%d "
MGLOG_W_ONCE("%s: VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT is unsupported for format=%d "
"textureId=%d; creating without it (per-slice framebuffer attachment will be "
"unavailable for it)",
__func__, static_cast<Int>(format), texture.GetExternalIndex());
@@ -1853,7 +1853,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VkResult createImageResult =
vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr);
if (createImageResult != VK_SUCCESS) {
MGLOG_F("SyncTextureResource: vmaCreateImage failed (%d) textureId=%d extent=%ux%u depth=%u layers=%u "
// E_ONCE, not F: the comment above says it - this is a soft failure the caller
// recovers from, and it re-fires on every sync of every texture the driver refuses.
MGLOG_E_ONCE("SyncTextureResource: vmaCreateImage failed (%d) textureId=%d extent=%ux%u depth=%u layers=%u "
"mips=%u samples=%d format=%d",
createImageResult, texture.GetExternalIndex(), imageInfo.extent.width, imageInfo.extent.height,
imageInfo.extent.depth, imageInfo.arrayLayers, imageInfo.mipLevels,
@@ -2426,7 +2428,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Bool srcIsD24S8 = outResource.format == VK_FORMAT_D24_UNORM_S8_UINT;
const Bool srcIsD32FS8 = outResource.format == VK_FORMAT_D32_SFLOAT_S8_UINT;
if (!srcIsD24S8 && !srcIsD32FS8) {
MGLOG_E("UploadDirtyMipLevels: unsupported combined depth-stencil format %d for textureId=%d",
MGLOG_E_ONCE("UploadDirtyMipLevels: unsupported combined depth-stencil format %d for textureId=%d",
static_cast<Int>(outResource.format), mipmapTexture.GetExternalIndex());
for (const auto& item : uploadItems) {
mipmapTexture.MarkStorageDirty(item.target, item.level, false);
@@ -15,7 +15,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(initInfo.device != VK_NULL_HANDLE, "VkTimerQueryManager::Initialize requires valid VkDevice");
MOBILEGL_ASSERT(initInfo.frameCount > 0, "VkTimerQueryManager::Initialize requires non-zero frame count");
if (initInfo.timestampValidBits == 0 || initInfo.timestampPeriodNs <= 0.0f || initInfo.slotsPerPool == 0) {
MGLOG_W("VkTimerQueryManager: timestamps unsupported (validBits=%u, period=%f, slots=%u)",
MGLOG_W_ONCE("VkTimerQueryManager: timestamps unsupported (validBits=%u, period=%f, slots=%u)",
initInfo.timestampValidBits, initInfo.timestampPeriodNs, initInfo.slotsPerPool);
return false;
}
@@ -35,7 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
for (auto& poolState : m_pools) {
const VkResult result = vkCreateQueryPool(m_device, &poolInfo, nullptr, &poolState.pool);
if (result != VK_SUCCESS) {
MGLOG_E("VkTimerQueryManager: vkCreateQueryPool failed with %s", VkResultToString(result));
MGLOG_E_ONCE("VkTimerQueryManager: vkCreateQueryPool failed with %s", VkResultToString(result));
Shutdown();
return false;
}
@@ -90,7 +90,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto& poolState = m_pools[frameIndex];
if (poolState.cursor >= m_slotsPerPool) {
if (!poolState.exhaustionWarned) {
MGLOG_W("VkTimerQueryManager: frame %u timestamp pool exhausted (%u slots); further timer queries "
MGLOG_W_ONCE("VkTimerQueryManager: frame %u timestamp pool exhausted (%u slots); further timer queries "
"this frame fall back to the frontend path",
frameIndex, m_slotsPerPool);
poolState.exhaustionWarned = true;
@@ -120,7 +120,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_device, m_pools[record.poolIndex].pool, record.slot, 1, sizeof(resultWithAvailability),
resultWithAvailability, sizeof(Uint64), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
if (result != VK_SUCCESS && result != VK_NOT_READY) {
MGLOG_E("VkTimerQueryManager: vkGetQueryPoolResults failed with %s", VkResultToString(result));
MGLOG_E_ONCE("VkTimerQueryManager: vkGetQueryPoolResults failed with %s", VkResultToString(result));
return false;
}
if (resultWithAvailability[1] == 0) {
File diff suppressed because it is too large Load Diff
@@ -74,6 +74,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The context line (__VA_ARGS__ = its own format string + args) must be a SEPARATE log
// call: appending its format to the base format while its arguments precede the base
// arguments makes every conversion read the wrong slot (a %s pulling an int crashes).
//
// MGLOG_F and deliberately NOT latched. VK_VERIFY is the invariant-check macro: a Vulkan call
// MobileGL believes it has already made legal came back non-success, which is a
// should-never-happen state, not an expected failure mode a user hits. Those fast-fail loudly
// and keep saying so - the log-quietness rules that latch W/E cover expected failures (driver
// capability gaps, app misuse), not broken internal invariants. MOBILEGL_ASSERT below traps in
// a DEBUG build; MGLOG_F is what makes the same condition visible in an INFO test run, where
// the assert is compiled out by contract.
//
// A soft, recoverable failure must therefore NOT be routed through VK_VERIFY. Check the
// VkResult directly and report it with MGLOG_E_ONCE - see VkTextureManager::SyncTextureResource,
// where a driver legitimately refuses an image the format pre-check accepted.
#define VK_VERIFY(expr, ...) \
do { \
VkResult _vk_verify_result = (expr); \
+11 -11
View File
@@ -21,7 +21,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
EGLStateContext* GetState() {
if (!MG_State::pEGLContext) {
MGLOG_E("pEGLContext is null. MG_State may not be initialized.");
MGLOG_E_ONCE("pEGLContext is null. MG_State may not be initialized.");
}
return MG_State::pEGLContext.get();
}
@@ -146,7 +146,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
state->DestroySurface(dpy, surface);
return EGL_NO_SURFACE;
}
@@ -172,11 +172,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
return EGL_FALSE;
}
if (!backendObject->SwapEGLBuffers(dpy, draw)) {
MGLOG_E("eglSwapBuffers failed on thread=%s dpy=%p draw=%p", CurrentThreadIdString().c_str(), dpy, draw);
MGLOG_E_ONCE("eglSwapBuffers failed on thread=%s dpy=%p draw=%p", CurrentThreadIdString().c_str(), dpy, draw);
state->SetError(EGL_BAD_SURFACE);
return EGL_FALSE;
}
@@ -211,7 +211,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
return EGL_FALSE;
}
if (!backendObject->InitializeEGLDisplay(dpy, major, minor)) {
@@ -265,7 +265,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (releaseCurrentRequest) {
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
if (!backendObject->MakeEGLCurrent(dpy, draw, read, ctx)) {
MGLOG_E("eglMakeCurrent release failed in backend thread=%s", threadId.c_str());
MGLOG_E_ONCE("eglMakeCurrent release failed in backend thread=%s", threadId.c_str());
state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext);
state->SetError(EGL_BAD_ACCESS);
return EGL_FALSE;
@@ -277,12 +277,12 @@ namespace MobileGL::MG_Impl::EGLImpl {
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext);
return EGL_FALSE;
}
if (!backendObject->MakeEGLCurrent(dpy, draw, read, ctx)) {
MGLOG_E("eglMakeCurrent backend attach failed thread=%s dpy=%p draw=%p read=%p ctx=%p", threadId.c_str(),
MGLOG_E_ONCE("eglMakeCurrent backend attach failed thread=%s dpy=%p draw=%p read=%p ctx=%p", threadId.c_str(),
dpy, draw, read, ctx);
state->SetError(EGL_BAD_ACCESS);
state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext);
@@ -703,7 +703,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
state->DestroySurface(dpy, surface);
return EGL_NO_SURFACE;
}
@@ -726,7 +726,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
}
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
MGLOG_E("activeBackendObject not initialized!");
MGLOG_E_ONCE("activeBackendObject not initialized!");
return EGL_FALSE;
}
width = std::max<EGLint>(width, 1);
@@ -764,7 +764,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
MGLOG_D("eglGetProcAddress(%s)", name);
void* proc = MG_Impl::GetProcAddress(name);
if (!proc) {
MGLOG_W("Failed to get function: %s", name);
MGLOG_D("Failed to get function: %s", name);
return nullptr;
}
return (__eglMustCastToProperFunctionPointerType)proc;
+1 -1
View File
@@ -149,7 +149,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// quietly writing a differently-sized pattern.
const SizeT sourceSize = MG_Util::GetInputBytesPerPixel(inputFormat, pixelType);
if (sourceSize != elementSize) {
MGLOG_W("%s: clear pattern is %zu bytes but internalformat 0x%X stores %zu; "
MGLOG_W_ONCE("%s: clear pattern is %zu bytes but internalformat 0x%X stores %zu; "
"converting between them is not implemented",
GetBufferOpName(op), sourceSize, internalformat, elementSize);
}
@@ -25,12 +25,12 @@
#define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
#define DECLARE_GL_FUNCTION_STUB_END(type, name, ...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
MGLOG_W_ONCE("Stub function: %s(...)", __FUNCTION__); \
return (type)1; \
}
#define DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(type, name, ...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
MGLOG_W_ONCE("Stub function: %s(...)", __FUNCTION__); \
}
#define DECLARE_GL_FUNCTION_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
@@ -2585,7 +2585,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTransformFeedbackNV, GLenum target, GLui
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)
MOBILEGL_GL_API GLboolean glIsTransformFeedbackNV(GLuint id) {
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
MGLOG_W_ONCE("Stub function: %s(...)", __FUNCTION__);
return GL_FALSE;
}
DECLARE_GL_FUNCTION_STUB_HEAD(void, PauseTransformFeedbackNV, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PauseTransformFeedbackNV, )
@@ -3181,5 +3181,5 @@ MOBILEGL_GL_API void glVertexAttribDivisorARB(GLuint index, GLuint divisor) {
}
MOBILEGL_GL_API void glWindowRectanglesEXT(GLenum mode, GLsizei count, const GLint* box) {
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
MGLOG_W_ONCE("Stub function: %s(...)", __FUNCTION__);
}
@@ -547,7 +547,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
auto blitNamedFramebuffer = MG_Backend::gBackendFunctionsTable.GL.BlitNamedFramebuffer;
if (!blitNamedFramebuffer) {
MGLOG_E("glBlitNamedFramebuffer skipped: backend does not implement explicit framebuffer blit.");
MGLOG_E_ONCE("glBlitNamedFramebuffer skipped: backend does not implement explicit framebuffer blit.");
return;
}
blitNamedFramebuffer(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
@@ -558,7 +558,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLenum buffer, GLint drawbuffer, const GLfloat* value) {
auto clearNamedFramebufferfv = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfv;
if (!clearNamedFramebufferfv) {
MGLOG_E("glClearNamedFramebufferfv skipped: backend does not implement explicit framebuffer clear.");
MGLOG_E_ONCE("glClearNamedFramebufferfv skipped: backend does not implement explicit framebuffer clear.");
return;
}
clearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value);
@@ -568,7 +568,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
auto clearNamedFramebufferfi = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfi;
if (!clearNamedFramebufferfi) {
MGLOG_E("glClearNamedFramebufferfi skipped: backend does not implement explicit framebuffer clear.");
MGLOG_E_ONCE("glClearNamedFramebufferfi skipped: backend does not implement explicit framebuffer clear.");
return;
}
clearNamedFramebufferfi(framebuffer, buffer, drawbuffer, depth, stencil);
@@ -578,7 +578,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLenum buffer, GLint drawbuffer, const GLint* value) {
auto clearNamedFramebufferiv = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferiv;
if (!clearNamedFramebufferiv) {
MGLOG_E("glClearNamedFramebufferiv skipped: backend does not implement explicit framebuffer clear.");
MGLOG_E_ONCE("glClearNamedFramebufferiv skipped: backend does not implement explicit framebuffer clear.");
return;
}
clearNamedFramebufferiv(framebuffer, buffer, drawbuffer, value);
@@ -588,7 +588,7 @@ namespace MobileGL::MG_Impl::GLImpl {
GLenum buffer, GLint drawbuffer, const GLuint* value) {
auto clearNamedFramebufferuiv = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferuiv;
if (!clearNamedFramebufferuiv) {
MGLOG_E("glClearNamedFramebufferuiv skipped: backend does not implement explicit framebuffer clear.");
MGLOG_E_ONCE("glClearNamedFramebufferuiv skipped: backend does not implement explicit framebuffer clear.");
return;
}
clearNamedFramebufferuiv(framebuffer, buffer, drawbuffer, value);
+4 -4
View File
@@ -383,7 +383,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MGLOG_D("glGetString, name: %s", MG_Util::ConvertGLEnumToString(name).c_str());
if (!activeBackendObject) {
MGLOG_E("activeBackendObject is not initialized!");
MGLOG_E_ONCE("activeBackendObject is not initialized!");
return (GLubyte*)"Unknown";
}
@@ -442,7 +442,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const auto& activeBackendObject = MG_Backend::pActiveBackendObject;
if (!activeBackendObject) {
MGLOG_E("activeBackendObject is not initialized!");
MGLOG_E_ONCE("activeBackendObject is not initialized!");
return (GLubyte*)"Unknown";
}
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
@@ -1954,7 +1954,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const auto& activeBackendObject = MG_Backend::pActiveBackendObject;
if (!activeBackendObject) {
MGLOG_E("activeBackendObject is not initialized!");
MGLOG_E_ONCE("activeBackendObject is not initialized!");
return;
}
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
@@ -2248,7 +2248,7 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = static_cast<GLint>(std::lround(dynamicParameters.MaxTextureMaxAnisotropy));
break;
default:
MGLOG_E("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname);
MGLOG_D("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname);
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetIntegerv",
std::format("Invalid enum: 0x{:X}", pname)));
@@ -908,7 +908,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const SizeT span = UniformStorageSpanInBytes(ttype, size);
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
offset + span > programObject->GetUBOSize()) {
MGLOG_E("%s: uniform at program %u location %d has no backing storage; returning nothing", __func__,
MGLOG_E_ONCE("%s: uniform at program %u location %d has no backing storage; returning nothing", __func__,
program, location);
return;
}
@@ -962,7 +962,7 @@ namespace MobileGL::MG_Impl::GLImpl {
const SizeT span = UniformStorageSpanInBytes(ttype, size);
if (pUBO == nullptr || offset == MG_State::GLState::ProgramObject::kInvalidUniformOffset ||
offset + span > programObject->GetUBOSize()) {
MGLOG_E("%s: uniform at program %u location %d has no backing storage; returning nothing", __func__,
MGLOG_E_ONCE("%s: uniform at program %u location %d has no backing storage; returning nothing", __func__,
program, location);
return;
}
@@ -1062,7 +1062,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!initialized) {
const auto& activeBackendObject = MG_Backend::pActiveBackendObject;
if (!activeBackendObject) {
MGLOG_E("activeBackendObject is not initialized!");
MGLOG_E_ONCE("activeBackendObject is not initialized!");
return;
}
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
@@ -1152,7 +1152,7 @@ namespace MobileGL::MG_Impl::GLImpl {
SizeT writeSize = ItemCount * sizeof(T);
if (size < writeSize) {
// Metadata bug: degrade to a clamped copy instead of killing the process.
MGLOG_E("%s: uniform size mismatch at program %u location %u: expected at least %zu bytes, got %zu "
MGLOG_E_ONCE("%s: uniform size mismatch at program %u location %u: expected at least %zu bytes, got %zu "
"bytes; clamping",
__func__, programObject.GetExternalIndex(), location, ItemCount * sizeof(T), size);
writeSize = size;
@@ -1173,7 +1173,7 @@ namespace MobileGL::MG_Impl::GLImpl {
offset + byteOffsetInsideUniform + writeSize > uboSize) {
// Should not happen: linking gives every settable uniform backing
// storage. Log and drop the write instead of faulting.
MGLOG_E("%s: uniform at program %u location %u has no backing storage (ubo=%p offset=%u size=%zu "
MGLOG_E_ONCE("%s: uniform at program %u location %u has no backing storage (ubo=%p offset=%u size=%zu "
"uboSize=%zu); dropping write",
__func__, programObject.GetExternalIndex(), location, static_cast<void*>(pUBO), offset,
writeSize, uboSize);
@@ -1807,7 +1807,7 @@ namespace MobileGL::MG_Impl::GLImpl {
break;
}
default:
MGLOG_E("%s: unknown pname = %p %s", __func__, pname, MG_Util::ConvertGLEnumToString(pname).c_str());
MGLOG_D("%s: unknown pname = %p %s", __func__, pname, MG_Util::ConvertGLEnumToString(pname).c_str());
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
+10 -9
View File
@@ -621,7 +621,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// the process down, which is never an acceptable answer to a query - see the same reasoning
// above for the compressed-format path.
void RecordUnsupportedLevelQueryStorage(const char* caller, GLenum pname) {
MGLOG_I("%s: glGetTexLevelParameter(pname=%s) is not implemented for texture-buffer "
MGLOG_W_ONCE("%s: glGetTexLevelParameter(pname=%s) is not implemented for texture-buffer "
"storage; recording GL_INVALID_OPERATION instead of terminating",
caller, MG_Util::ConvertGLEnumToString(pname).c_str());
MG_State::pGLContext->RecordError(
@@ -870,7 +870,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_Util::GetInputBytesPerPixel(MG_Util::ConvertGLEnumToTextureInputFormat(format),
MG_Util::ConvertGLEnumToTexturePixelDataType(type));
if (readBytesPerTexel != bytesPerTexel) {
MGLOG_I("%s: cannot copy into a %zu-byte texel from a %zu-byte readback layout", caller,
MGLOG_W_ONCE("%s: cannot copy into a %zu-byte texel from a %zu-byte readback layout", caller,
bytesPerTexel, readBytesPerTexel);
return false;
}
@@ -1490,7 +1490,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
yoffset + height > static_cast<GLsizei>(texelSize.y()) ||
zoffset + depth > static_cast<GLsizei>(texelSize.z())) {
MGLOG_E("TexSubImage3D_State: Specified region exceeds texture level dimensions");
MGLOG_E_ONCE("TexSubImage3D_State: Specified region exceeds texture level dimensions");
free(processedPixels);
return;
}
@@ -1599,7 +1599,7 @@ namespace MobileGL::MG_Impl::GLImpl {
{width, height, 1}, false, inputSize);
if (!processedPixels || inputSize == 0) {
MGLOG_E("TexSubImage2D_State: Failed to process pixel data for TexSubImage2D, width: %d, height: %d", width,
MGLOG_E_ONCE("TexSubImage2D_State: Failed to process pixel data for TexSubImage2D, width: %d, height: %d", width,
height);
if (processedPixels) free(processedPixels);
return;
@@ -1613,7 +1613,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
yoffset + height > static_cast<GLsizei>(texelSize.y())) {
MGLOG_E("TexSubImage2D_State: Specified region exceeds texture dimensions");
MGLOG_E_ONCE("TexSubImage2D_State: Specified region exceeds texture dimensions");
free(processedPixels);
return;
}
@@ -2164,7 +2164,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (processedPixels && imageSize > 0) {
if (imageSize != internalBytes) {
MGLOG_W("%s: Processed pixel data size (%zu) does not match expected size (%zu). "
MGLOG_W_ONCE("%s: Processed pixel data size (%zu) does not match expected size (%zu). "
"This may indicate an alignment or processing issue.",
__func__, imageSize, internalBytes);
}
@@ -2310,7 +2310,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (processedPixels && imageSize > 0) {
if (imageSize != internalBytes) {
MGLOG_W("TexImage2D_State: Processed pixel data size (%zu) does not match expected size (%zu). "
MGLOG_W_ONCE("TexImage2D_State: Processed pixel data size (%zu) does not match expected size (%zu). "
"This may indicate an alignment or processing issue.",
imageSize, internalBytes);
}
@@ -3643,10 +3643,11 @@ namespace MobileGL::MG_Impl::GLImpl {
// an application has every right to expect from it. Before this existed the call answered
// GL_INVALID_ENUM, which was wrong but at least visible; a silent success that leaves the
// sampled texels untouched is the kind of thing that costs a day to find from the other
// end. MGLOG_I, not _W: warnings are compiled out at the level everything ships at.
// end. MGLOG_W is the right level and now survives at INFO; it sat at MGLOG_I only
// while the Log.h ordering compiled warnings out of the builds that ship.
static std::atomic<Bool> announcedNoCodec{false};
if (!announcedNoCodec.exchange(true)) {
MGLOG_I("%s: the compressed blocks are stored verbatim and returned by "
MGLOG_W("%s: the compressed blocks are stored verbatim and returned by "
"glGetCompressedTexImage, but there is no BC/ETC decoder here, so they do not "
"reach the texels this level SAMPLES as. Upload through glTexSubImage2D for "
"that.",
@@ -527,7 +527,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!MG_Backend::pActiveBackendObject ||
!MG_Backend::pActiveBackendObject->GetDynamicParameters().SupportsFloat64VertexAttributes) {
MGLOG_I("VertexAttribLFormat: attribute %u asked for a 64-bit (GL_DOUBLE) format, but this "
MGLOG_W_ONCE("VertexAttribLFormat: attribute %u asked for a 64-bit (GL_DOUBLE) format, but this "
"backend has no double-precision vertex attribute support - see the "
"\"64-bit vertex attributes\" / \"shaderFloat64\" POST row for what that costs",
attribindex);
@@ -166,32 +166,32 @@ MOBILEGL_GLX_API int glXSwapIntervalSGI(int interval) {
// Legacy entry points some loaders probe for; harmless no-op stubs.
MOBILEGL_GLX_API void glXCopyContext(Display*, void*, void*, unsigned long) {
MGLOG_W("glx: glXCopyContext is not supported");
MGLOG_W_ONCE("glx: glXCopyContext is not supported");
}
MOBILEGL_GLX_API unsigned long glXCreateGLXPixmap(Display*, void*, unsigned long) {
MGLOG_W("glx: glXCreateGLXPixmap is not supported");
MGLOG_W_ONCE("glx: glXCreateGLXPixmap is not supported");
return 0;
}
MOBILEGL_GLX_API void glXDestroyGLXPixmap(Display*, unsigned long) {}
MOBILEGL_GLX_API unsigned long glXCreatePixmap(Display*, void*, unsigned long, const int*) {
MGLOG_W("glx: glXCreatePixmap is not supported");
MGLOG_W_ONCE("glx: glXCreatePixmap is not supported");
return 0;
}
MOBILEGL_GLX_API void glXDestroyPixmap(Display*, unsigned long) {}
MOBILEGL_GLX_API unsigned long glXCreatePbuffer(Display*, void*, const int*) {
MGLOG_W("glx: glXCreatePbuffer is not supported");
MGLOG_W_ONCE("glx: glXCreatePbuffer is not supported");
return 0;
}
MOBILEGL_GLX_API void glXDestroyPbuffer(Display*, unsigned long) {}
MOBILEGL_GLX_API void glXUseXFont(unsigned long, int, int, int) {
MGLOG_W("glx: glXUseXFont is not supported");
MGLOG_W_ONCE("glx: glXUseXFont is not supported");
}
MOBILEGL_GLX_API void glXSelectEvent(Display*, unsigned long, unsigned long) {}
+9 -9
View File
@@ -149,7 +149,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
fns->Sync = reinterpret_cast<decltype(fns->Sync)>(dlsym(fns->Library, "XSync"));
}
if (!fns->Valid()) {
MGLOG_E("glx: failed to load libX11 entry points");
MGLOG_E_ONCE("glx: failed to load libX11 entry points");
}
return fns;
}();
@@ -314,7 +314,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
Uint32 width = 0;
Uint32 height = 0;
if (!QueryDrawableSize(dpy, drawable, width, height)) {
MGLOG_E("glx: XGetGeometry failed for drawable 0x%lx", drawable);
MGLOG_E_ONCE("glx: XGetGeometry failed for drawable 0x%lx", drawable);
return nullptr;
}
@@ -326,7 +326,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
EGLSurface surface = EGLImpl::CreatePlatformWindowSurface(
context.Display, context.Config, reinterpret_cast<void*>(drawable), attribs);
if (surface == EGL_NO_SURFACE) {
MGLOG_E("glx: failed to create window surface for drawable 0x%lx (%ux%u)", drawable,
MGLOG_E_ONCE("glx: failed to create window surface for drawable 0x%lx (%ux%u)", drawable,
width, height);
return nullptr;
}
@@ -347,7 +347,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
const std::lock_guard<std::recursive_mutex> lock(RegistryMutex());
EGLDisplay display = EnsureDisplay();
if (display == EGL_NO_DISPLAY) {
MGLOG_E("glx: no EGL display");
MGLOG_E_ONCE("glx: no EGL display");
return nullptr;
}
EGLImpl::BindAPI(EGL_OPENGL_API);
@@ -376,13 +376,13 @@ namespace MobileGL::MG_Impl::GLXImpl {
EGLint configCount = 0;
if (!EGLImpl::ChooseConfig(display, configAttribs, &config, 1, &configCount) ||
configCount <= 0) {
MGLOG_E("glx: eglChooseConfig failed");
MGLOG_E_ONCE("glx: eglChooseConfig failed");
return nullptr;
}
EGLContext eglContext = EGLImpl::CreateContext(display, config, shareContext, contextAttribs);
if (eglContext == EGL_NO_CONTEXT) {
MGLOG_E("glx: eglCreateContext failed");
MGLOG_E_ONCE("glx: eglCreateContext failed");
return nullptr;
}
@@ -931,7 +931,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
if (!EGLImpl::MakeCurrent(object->Display, surface->Surface, surface->Surface,
object->Context)) {
MGLOG_E("glx: eglMakeCurrent failed (drawable=0x%lx, ctx=%p)", drawable, context);
MGLOG_E_ONCE("glx: eglMakeCurrent failed (drawable=0x%lx, ctx=%p)", drawable, context);
return 0;
}
t_current = {dpy, drawable, drawable, context};
@@ -943,7 +943,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
if (context && draw != read) {
// MobileGL's backends reject split draw/read surfaces; bind the draw
// drawable for both, which is what every real caller here needs.
MGLOG_W("glx: glXMakeContextCurrent draw 0x%lx != read 0x%lx, using draw for both", draw,
MGLOG_W_ONCE("glx: glXMakeContextCurrent draw 0x%lx != read 0x%lx, using draw for both", draw,
read);
}
const int result = MakeCurrent(dpy, draw, context);
@@ -958,7 +958,7 @@ namespace MobileGL::MG_Impl::GLXImpl {
auto& surfaces = DrawableSurfaces();
auto it = surfaces.find(drawable);
if (it == surfaces.end()) {
MGLOG_W("glx: glXSwapBuffers with no surface for drawable 0x%lx", drawable);
MGLOG_W_ONCE("glx: glXSwapBuffers with no surface for drawable 0x%lx", drawable);
return;
}
SyncSurfaceSize(dpy, drawable, it->second);
+1 -1
View File
@@ -31,7 +31,7 @@ namespace MG_Impl::GLXImpl {
#endif
void* proc = MobileGL::MG_Impl::GetProcAddress(name);
if (!proc) {
MGLOG_W("Failed to get function: %s", (const char*)name);
MGLOG_D("Failed to get function: %s", (const char*)name);
return nullptr;
}
+1 -1
View File
@@ -1403,7 +1403,7 @@ namespace MobileGL::MG_Impl {
GETPROC(glFramebufferTextureMultiviewOVR, name);
// GETPROC(glNamedFramebufferTextureMultiviewOVR, name);
MGLOG_W("GetProcAddress(%s) = nullptr!", name);
MGLOG_D("GetProcAddress(%s) = nullptr!", name);
return nullptr;
}
} // namespace MobileGL::MG_Impl
@@ -269,7 +269,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
}
id metalLayerClass = reinterpret_cast<id>(objc_getClass("CAMetalLayer"));
if (!metalLayerClass) {
MGLOG_E("NSOpenGLImpl: CAMetalLayer class not found");
MGLOG_E_ONCE("NSOpenGLImpl: CAMetalLayer class not found");
return nil;
}
@@ -310,7 +310,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
static_cast<GLint>(geometry.DrawableSize.width),
static_cast<GLint>(geometry.DrawableSize.height));
if (error != kCGLNoError) {
MGLOG_E("NSOpenGLImpl: failed to attach drawable: %s", CGLImpl::ErrorString(error));
MGLOG_E_ONCE("NSOpenGLImpl: failed to attach drawable: %s", CGLImpl::ErrorString(error));
}
}
@@ -325,7 +325,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
}
const auto error = CGLImpl::SetCurrentContext(context);
if (error != kCGLNoError) {
MGLOG_E("NSOpenGLImpl: makeCurrentContext failed: %s", CGLImpl::ErrorString(error));
MGLOG_E_ONCE("NSOpenGLImpl: makeCurrentContext failed: %s", CGLImpl::ErrorString(error));
}
}
@@ -345,7 +345,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
}
const auto error = CGLImpl::FlushDrawable(context);
if (error != kCGLNoError) {
MGLOG_E("NSOpenGLImpl: flushBuffer failed: %s", CGLImpl::ErrorString(error));
MGLOG_E_ONCE("NSOpenGLImpl: flushBuffer failed: %s", CGLImpl::ErrorString(error));
}
}
@@ -377,7 +377,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
static_cast<GLint>(geometry.DrawableSize.width),
static_cast<GLint>(geometry.DrawableSize.height));
if (error != kCGLNoError) {
MGLOG_E("NSOpenGLImpl: update failed to attach drawable: %s", CGLImpl::ErrorString(error));
MGLOG_E_ONCE("NSOpenGLImpl: update failed to attach drawable: %s", CGLImpl::ErrorString(error));
return;
}
CGLImpl::UpdateContext(context);
@@ -421,7 +421,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
SEL selector = sel_registerName(selectorName);
Method method = class_getInstanceMethod(cls, selector);
if (!method) {
MGLOG_W("NSOpenGLImpl: missing instance method %s", selectorName);
MGLOG_W_ONCE("NSOpenGLImpl: missing instance method %s", selectorName);
return;
}
if (original) {
@@ -434,7 +434,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
SEL selector = sel_registerName(selectorName);
Method method = class_getClassMethod(cls, selector);
if (!method) {
MGLOG_W("NSOpenGLImpl: missing class method %s", selectorName);
MGLOG_W_ONCE("NSOpenGLImpl: missing class method %s", selectorName);
return;
}
method_setImplementation(method, replacement);
@@ -444,7 +444,7 @@ namespace MobileGL::MG_Impl::NSOpenGLImpl {
Class pixelFormatClass = objc_getClass("NSOpenGLPixelFormat");
Class contextClass = objc_getClass("NSOpenGLContext");
if (!pixelFormatClass || !contextClass) {
MGLOG_W("NSOpenGLImpl: NSOpenGL classes are not loaded; hooks not installed");
MGLOG_W_ONCE("NSOpenGLImpl: NSOpenGL classes are not loaded; hooks not installed");
return false;
}
@@ -56,7 +56,7 @@ extern "C" HGLRC WINAPI wglCreateLayerContext(HDC hdc, int iLayerPlane) {
}
extern "C" BOOL WINAPI wglCopyContext(HGLRC, HGLRC, UINT) {
MGLOG_W("wglCopyContext is not supported");
MGLOG_W_ONCE("wglCopyContext is not supported");
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
@@ -132,24 +132,24 @@ extern "C" DWORD WINAPI wglSwapMultipleBuffers(UINT n, CONST WGLSWAP* ps) {
// ---- Font rendering (legacy immediate-mode feature; not supported) ----
extern "C" BOOL WINAPI wglUseFontBitmapsA(HDC, DWORD, DWORD, DWORD) {
MGLOG_W("wglUseFontBitmapsA is not supported");
MGLOG_W_ONCE("wglUseFontBitmapsA is not supported");
return FALSE;
}
extern "C" BOOL WINAPI wglUseFontBitmapsW(HDC, DWORD, DWORD, DWORD) {
MGLOG_W("wglUseFontBitmapsW is not supported");
MGLOG_W_ONCE("wglUseFontBitmapsW is not supported");
return FALSE;
}
extern "C" BOOL WINAPI wglUseFontOutlinesA(HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, int,
LPGLYPHMETRICSFLOAT) {
MGLOG_W("wglUseFontOutlinesA is not supported");
MGLOG_W_ONCE("wglUseFontOutlinesA is not supported");
return FALSE;
}
extern "C" BOOL WINAPI wglUseFontOutlinesW(HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, int,
LPGLYPHMETRICSFLOAT) {
MGLOG_W("wglUseFontOutlinesW is not supported");
MGLOG_W_ONCE("wglUseFontOutlinesW is not supported");
return FALSE;
}
+7 -7
View File
@@ -215,7 +215,7 @@ namespace MobileGL::MG_Impl::WGLImpl {
Uint32 width = 0;
Uint32 height = 0;
if (!QueryClientSize(hwnd, width, height)) {
MGLOG_E("wgl: GetClientRect failed for HWND %p", hwnd);
MGLOG_E_ONCE("wgl: GetClientRect failed for HWND %p", hwnd);
return nullptr;
}
@@ -227,7 +227,7 @@ namespace MobileGL::MG_Impl::WGLImpl {
EGLSurface surface =
EGLImpl::CreatePlatformWindowSurface(context.Display, context.Config, hwnd, attribs);
if (surface == EGL_NO_SURFACE) {
MGLOG_E("wgl: failed to create window surface for HWND %p (%ux%u)", hwnd, width, height);
MGLOG_E_ONCE("wgl: failed to create window surface for HWND %p (%ux%u)", hwnd, width, height);
return nullptr;
}
@@ -244,7 +244,7 @@ namespace MobileGL::MG_Impl::WGLImpl {
const std::lock_guard<std::recursive_mutex> lock(RegistryMutex());
EGLDisplay display = EnsureDisplay();
if (display == EGL_NO_DISPLAY) {
MGLOG_E("wgl: no EGL display");
MGLOG_E_ONCE("wgl: no EGL display");
return nullptr;
}
EGLImpl::BindAPI(EGL_OPENGL_API);
@@ -275,13 +275,13 @@ namespace MobileGL::MG_Impl::WGLImpl {
EGLConfig config = nullptr;
EGLint configCount = 0;
if (!EGLImpl::ChooseConfig(display, configAttribs, &config, 1, &configCount) || configCount <= 0) {
MGLOG_E("wgl: eglChooseConfig failed");
MGLOG_E_ONCE("wgl: eglChooseConfig failed");
return nullptr;
}
EGLContext eglContext = EGLImpl::CreateContext(display, config, shareContext, contextAttribs);
if (eglContext == EGL_NO_CONTEXT) {
MGLOG_E("wgl: eglCreateContext failed");
MGLOG_E_ONCE("wgl: eglCreateContext failed");
return nullptr;
}
@@ -612,7 +612,7 @@ namespace MobileGL::MG_Impl::WGLImpl {
auto& surfaces = WindowSurfaces();
auto it = surfaces.find(hwnd);
if (it == surfaces.end()) {
MGLOG_W("wglSwapBuffers: no surface for HWND %p", hwnd);
MGLOG_W_ONCE("wglSwapBuffers: no surface for HWND %p", hwnd);
return FALSE;
}
SyncSurfaceSize(hwnd, it->second);
@@ -685,7 +685,7 @@ namespace MobileGL::MG_Impl::WGLImpl {
}
if (!EGLImpl::MakeCurrent(object->Display, surface->Surface, surface->Surface, object->Context)) {
MGLOG_E("wglMakeCurrent: eglMakeCurrent failed (hdc=%p, hglrc=%p)", hdc, hglrc);
MGLOG_E_ONCE("wglMakeCurrent: eglMakeCurrent failed (hdc=%p, hglrc=%p)", hdc, hglrc);
return FALSE;
}
t_current = {hdc, hglrc};
+4 -4
View File
@@ -196,7 +196,7 @@ namespace MobileGL::MG_State {
// (which expands to nothing outside debug builds).
void GLContext::SetCurrentVertexAttributeFloat(Uint index, const Array<Float, 4>& value) {
if (index >= m_currentVertexAttributes.size()) {
MGLOG_E("SetCurrentVertexAttributeFloat: index %u is out of range", index);
MGLOG_E_ONCE("SetCurrentVertexAttributeFloat: index %u is out of range", index);
return;
}
@@ -210,7 +210,7 @@ namespace MobileGL::MG_State {
void GLContext::SetCurrentVertexAttributeInt(Uint index, const Array<Int32, 4>& value) {
if (index >= m_currentVertexAttributes.size()) {
MGLOG_E("SetCurrentVertexAttributeInt: index %u is out of range", index);
MGLOG_E_ONCE("SetCurrentVertexAttributeInt: index %u is out of range", index);
return;
}
@@ -224,7 +224,7 @@ namespace MobileGL::MG_State {
void GLContext::SetCurrentVertexAttributeUint(Uint index, const Array<Uint32, 4>& value) {
if (index >= m_currentVertexAttributes.size()) {
MGLOG_E("SetCurrentVertexAttributeUint: index %u is out of range", index);
MGLOG_E_ONCE("SetCurrentVertexAttributeUint: index %u is out of range", index);
return;
}
@@ -239,7 +239,7 @@ namespace MobileGL::MG_State {
const CurrentVertexAttributeValue& GLContext::GetCurrentVertexAttribute(Uint index) const {
static const CurrentVertexAttributeValue defaultValue{};
if (index >= m_currentVertexAttributes.size()) {
MGLOG_E("GetCurrentVertexAttribute: index %u is out of range", index);
MGLOG_E_ONCE("GetCurrentVertexAttribute: index %u is out of range", index);
return defaultValue;
}
return m_currentVertexAttributes[index];
@@ -14,10 +14,10 @@
namespace MobileGL::MG_State::GLState {
void ErrorState::RecordError(ErrorCode code, UniquePtr<ErrorInfo> info) {
if (code == ErrorCode::NoError) {
MGLOG_E("Recording Non-OpenGL error:\n%s", info->toString().c_str());
MGLOG_D("Recording Non-OpenGL error:\n%s", info->toString().c_str());
m_nonGLErrors.push_back(MakeUnique<Error>(code, Move(info)));
} else {
MGLOG_E("Recording OpenGL error (%s):\n%s",
MGLOG_D("Recording OpenGL error (%s):\n%s",
MG_Util::ConvertGLEnumToString(MG_Util::ConvertErrorCodeToGLEnum(code)).c_str(),
info->toString().c_str());
// GL error semantics are sticky flags, not a queue (GL 3.3 core §2.5): with multiple
@@ -255,7 +255,7 @@ namespace MobileGL::MG_State::GLState {
static_cast<SizeT>(offset) + write.byteOffsetInUniform + write.byteSize > uboSize) {
// Same verdict the live write path reaches for a uniform without backing
// storage: log and drop, rather than fault.
MGLOG_E("ProgramObject %u: buffered uniform write at location %u has no backing storage "
MGLOG_E_ONCE("ProgramObject %u: buffered uniform write at location %u has no backing storage "
"(offset=%u size=%u uboSize=%zu); dropping write",
m_externalIndex, write.location, offset, write.byteSize, uboSize);
continue;
@@ -436,7 +436,7 @@ namespace MobileGL::MG_State::GLState {
defaultFS->Compile(); // TODO: use a global default FS object.
auto status = defaultFS->GetCompileStatus();
if (!status) {
MGLOG_E("ProgramObject %u: Failed to compile default fragment shader. InfoLog:\n%s", m_externalIndex,
MGLOG_E_ONCE("ProgramObject %u: Failed to compile default fragment shader. InfoLog:\n%s", m_externalIndex,
defaultFS->GetInfoLog().c_str());
return;
}
+20
View File
@@ -16,5 +16,25 @@ target_link_libraries(
${LINK_LIBRARIES}
)
# The log-severity ordering and the MOBILEGL_ASSERT gate keyed to it. Links gtest
# (not gtest_main): the suite needs its own main() to point MOBILEGL_LOG_FILE_PATH at a
# temp file before anything in the process logs and latches the sink's FILE*.
add_executable(
LogLevelTest
LogLevelTest.cpp
)
target_include_directories(LogLevelTest PRIVATE
${MGL_ROOT}/include
${MGL_ROOT}/MobileGL
)
target_link_libraries(
LogLevelTest PRIVATE
GTest::gtest
${LINK_LIBRARIES}
)
include(GoogleTest)
gtest_discover_tests(JobNodeTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit)
gtest_discover_tests(LogLevelTest DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit)
+204
View File
@@ -0,0 +1,204 @@
// MobileGL - MobileGL/MG_Test/Util/LogLevelTest.cpp
// 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
// Guards the log-severity ordering and the MOBILEGL_ASSERT gate that hangs off it.
//
// Until 2026-08-13 the numeric order was DEBUG < WARN < ERROR < INFO < FATAL, so the
// production gate `#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_X` compiled
// MGLOG_W and MGLOG_E out of every INFO build. Failures logged with MGLOG_E were
// invisible in exactly the builds that shipped. Nothing in the suite noticed, which is
// why this file exists.
//
// This test is written to be meaningful in BOTH configurations - build it at
// MOBILEGL_LOG_LEVEL_INFO and at MOBILEGL_LOG_LEVEL_DEBUG and it checks the contract
// appropriate to each. It runs headless: no GL context, no device, just the file sink.
#include <gtest/gtest.h>
#include <Defines.h>
#include <MG_Util/Debug/Log.h>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
namespace {
// ---------------------------------------------------------------------------
// Compile-time contract
// ---------------------------------------------------------------------------
// The ordering itself. A renumbering that re-inverts the scale fails here.
static_assert(MOBILEGL_LOG_LEVEL_DEBUG < MOBILEGL_LOG_LEVEL_INFO, "DEBUG must be below INFO");
static_assert(MOBILEGL_LOG_LEVEL_INFO < MOBILEGL_LOG_LEVEL_WARN, "INFO must be below WARN");
static_assert(MOBILEGL_LOG_LEVEL_WARN < MOBILEGL_LOG_LEVEL_ERROR, "WARN must be below ERROR");
static_assert(MOBILEGL_LOG_LEVEL_ERROR < MOBILEGL_LOG_LEVEL_FATAL, "ERROR must be below FATAL");
// DEBUG must stay the floor: the MOBILEGL_ASSERT gate in Defines.h is spelled
// `ACTIVE <= MOBILEGL_LOG_LEVEL_DEBUG` and means "only in a DEBUG build". That
// reading is only correct while DEBUG is the minimum.
static_assert(MOBILEGL_LOG_LEVEL_DEBUG == 0, "DEBUG must be the lowest level");
// Defines.h and Log.h each define the five constants. Log.h's copy wins when both
// are included; if the two ever drift, the duplicate-definition warning is not
// guaranteed to be an error, so pin the values a second time from this TU's view.
static_assert(MOBILEGL_LOG_LEVEL_INFO == 1, "INFO must be 1 in both Defines.h and Log.h");
static_assert(MOBILEGL_LOG_LEVEL_WARN == 2, "WARN must be 2 in both Defines.h and Log.h");
static_assert(MOBILEGL_LOG_LEVEL_ERROR == 3, "ERROR must be 3 in both Defines.h and Log.h");
static_assert(MOBILEGL_LOG_LEVEL_FATAL == 4, "FATAL must be 4 in both Defines.h and Log.h");
// Whether this translation unit was compiled with asserts live. This is a literal
// copy of the Defines.h gate - the point of the test is to prove it agrees with
// MGLOG_D's liveness, observed at runtime below.
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
constexpr bool kAssertsLive = true;
#else
constexpr bool kAssertsLive = false;
#endif
// Whether the build is the production INFO configuration.
constexpr bool kBuiltAtInfo = (MOBILEGL_LOG_ACTIVE_LEVEL == MOBILEGL_LOG_LEVEL_INFO);
// ---------------------------------------------------------------------------
// Runtime observation of the sink
// ---------------------------------------------------------------------------
// The log file path is latched by MG_Util::Debug::InitFile() on the first write and
// never reopened, so the whole process gets one file. main() below points
// MOBILEGL_LOG_FILE_PATH at a temp file before gtest runs; this fixture emits one
// line per level and then reads the file back.
std::string g_logPath;
struct Emitted {
bool debug = false;
bool info = false;
bool warn = false;
bool error = false;
bool fatal = false;
};
Emitted EmitAndRead() {
// Distinctive markers so a substring search cannot collide with unrelated output.
MGLOG_D("MGLOGTEST_MARKER_DEBUG_5f3a");
MGLOG_I("MGLOGTEST_MARKER_INFO_5f3a");
MGLOG_W("MGLOGTEST_MARKER_WARN_5f3a");
MGLOG_E("MGLOGTEST_MARKER_ERROR_5f3a");
MGLOG_F("MGLOGTEST_MARKER_FATAL_5f3a");
std::ifstream in(g_logPath, std::ios::binary);
std::ostringstream ss;
ss << in.rdbuf();
const std::string text = ss.str();
Emitted e;
e.debug = text.find("MGLOGTEST_MARKER_DEBUG_5f3a") != std::string::npos;
e.info = text.find("MGLOGTEST_MARKER_INFO_5f3a") != std::string::npos;
e.warn = text.find("MGLOGTEST_MARKER_WARN_5f3a") != std::string::npos;
e.error = text.find("MGLOGTEST_MARKER_ERROR_5f3a") != std::string::npos;
e.fatal = text.find("MGLOGTEST_MARKER_FATAL_5f3a") != std::string::npos;
return e;
}
TEST(LogLevel, SinkIsReachableAtAll) {
// Guards the test itself: if the file sink were disabled or the path override
// ignored, every "level X is suppressed" assertion below would pass vacuously.
ASSERT_FALSE(g_logPath.empty()) << "test harness did not set MOBILEGL_LOG_FILE_PATH";
const Emitted e = EmitAndRead();
EXPECT_TRUE(e.fatal) << "FATAL is compiled in at every level; an empty log means the "
"file sink never opened and this suite proves nothing";
}
TEST(LogLevel, ProductionBuildKeepsErrorAndWarn) {
if constexpr (!kBuiltAtInfo) {
GTEST_SKIP() << "only meaningful when built at MOBILEGL_LOG_LEVEL_INFO";
} else {
const Emitted e = EmitAndRead();
// The regression this file exists for.
EXPECT_TRUE(e.error) << "MGLOG_E must be live in an INFO build";
EXPECT_TRUE(e.warn) << "MGLOG_W must be live in an INFO build";
EXPECT_TRUE(e.info) << "MGLOG_I must be live in an INFO build";
EXPECT_TRUE(e.fatal) << "MGLOG_F must be live in an INFO build";
// ...and the other half: D must still be compiled out, or production pays
// for every dev-only diagnostic in the tree.
EXPECT_FALSE(e.debug) << "MGLOG_D must be compiled out of an INFO build";
}
}
TEST(LogLevel, DebugBuildKeepsEverything) {
if constexpr (MOBILEGL_LOG_ACTIVE_LEVEL != MOBILEGL_LOG_LEVEL_DEBUG) {
GTEST_SKIP() << "only meaningful when built at MOBILEGL_LOG_LEVEL_DEBUG";
} else {
const Emitted e = EmitAndRead();
EXPECT_TRUE(e.debug);
EXPECT_TRUE(e.info);
EXPECT_TRUE(e.warn);
EXPECT_TRUE(e.error);
EXPECT_TRUE(e.fatal);
}
}
// ---------------------------------------------------------------------------
// The assert contract
// ---------------------------------------------------------------------------
TEST(LogLevel, AssertGateTracksDebugLiveness) {
// The contract: "INFO builds: asserts OFF; DEBUG builds: asserts ON". Stated
// without naming a level, that is exactly "asserts are live iff MGLOG_D is
// live" - which is checkable in whichever configuration this was built in,
// and is what makes the renumbering safe.
const Emitted e = EmitAndRead();
EXPECT_EQ(kAssertsLive, e.debug)
<< "MOBILEGL_ASSERT liveness (" << kAssertsLive << ") disagrees with MGLOG_D liveness ("
<< e.debug << "). The Defines.h assert gate and the Log.h MGLOG_D gate have drifted.";
}
TEST(LogLevel, AssertIsCompiledOutOfProductionBuilds) {
if constexpr (kAssertsLive) {
GTEST_SKIP() << "asserts are live in this configuration; see AssertIsLiveInDebugBuilds";
} else {
// If MOBILEGL_ASSERT were live here this would TRAP and take the process
// down, which is the behavioural half of the contract.
MOBILEGL_ASSERT(false, "this assert must be compiled out at %s", "INFO");
SUCCEED();
}
}
TEST(LogLevel, AssertIsLiveInDebugBuilds) {
if constexpr (!kAssertsLive) {
GTEST_SKIP() << "asserts are compiled out in this configuration";
} else {
// A satisfied assert must be a no-op rather than a trap; that it expands to
// real code at all is what kAssertsLive already established.
MOBILEGL_ASSERT(true, "a satisfied assert must not trap");
SUCCEED();
}
}
} // namespace
int main(int argc, char** argv) {
// Must happen before anything logs: MG_Util::Debug::InitFile() reads the variable
// once, on the first write, and caches the FILE*.
namespace fs = std::filesystem;
const fs::path path = fs::temp_directory_path() / "mobilegl-loglevel-test.log";
std::error_code ec;
fs::remove(path, ec);
g_logPath = path.string();
#if defined(_WIN32)
_putenv_s("MOBILEGL_LOG_FILE_PATH", g_logPath.c_str());
#else
setenv("MOBILEGL_LOG_FILE_PATH", g_logPath.c_str(), 1);
#endif
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+3 -3
View File
@@ -32,11 +32,11 @@ namespace MobileGL::MG_Util::Async {
try {
continuation();
} catch (const std::exception& e) {
MGLOG_E("JobNode: a terminal continuation threw (%s); it has been contained, but whatever it "
MGLOG_E_ONCE("JobNode: a terminal continuation threw (%s); it has been contained, but whatever it "
"was going to do did not happen",
e.what());
} catch (...) {
MGLOG_E("JobNode: a terminal continuation threw a non-std exception; it has been contained, "
MGLOG_E_ONCE("JobNode: a terminal continuation threw a non-std exception; it has been contained, "
"but whatever it was going to do did not happen");
}
}
@@ -165,7 +165,7 @@ namespace MobileGL::MG_Util::Async {
Vector<String> lines;
lines.swap(node.diagnostics.logLines);
for (const String& line : lines) {
MGLOG_W("%s", line.c_str());
MGLOG_D("%s", line.c_str());
}
}
+1 -1
View File
@@ -281,7 +281,7 @@ namespace MobileGL::MG_Util::Async {
enqueued = true;
}
} catch (...) {
MGLOG_E("ShaderCompilePool::Post: enqueue failed; cancelling the job so its joiner "
MGLOG_E_ONCE("ShaderCompilePool::Post: enqueue failed; cancelling the job so its joiner "
"cannot block forever");
if (node) node->Cancel();
return;
@@ -594,9 +594,10 @@ namespace MobileGL::MG_Util::BackendLoader {
#endif // !_WIN32
if (!eglLib) {
// MGLOG_F, not MGLOG_E: at the INFO log level every shipping and CI build
// uses, MGLOG_E is compiled out (Log.h orders DEBUG < WARN < ERROR < INFO),
// so this diagnosis was invisible in precisely the builds that needed it.
// MGLOG_F, not MGLOG_E: with no EGL there is no rendering at all, so this is a
// bring-up abort rather than a recoverable error. It was forced to F while the
// Log.h ordering compiled MGLOG_E out of every shipping and CI build; F is still
// the right level on its own merits, so it stays.
MGLOG_F("Failed to open EGL library: none of libEGL.so.1 / libEGL.so could be "
"dlopened; every EGL entry point will be null");
return;
@@ -986,6 +987,12 @@ namespace MobileGL::MG_Util::BackendLoader {
caps.SupportsBaseInstance ? "yes" : "no");
MGLOG_I(" clip distances (EXT_clip_cull_distance): %s", caps.SupportsClipDistance ? "yes" : "no");
// LOAD-BEARING STRING, not just a banner. android-plugin/trace-replay-ci.sh's
// is_angle_surface_lost() greps mobilegl.log for exactly "OpenGL ES capabilities:" to
// decide whether MobileGL got far enough to have a working context: if the probe ran,
// a later surface loss is a real defect rather than an emulator fault worth retrying.
// Demoting this line, renaming it, or moving it before the context is usable silently
// inverts that retry logic. It is init-phase, so MGLOG_I is correct and it stays.
MGLOG_I("OpenGL ES capabilities:");
glesFuncs.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.UniformBufferOffsetAlignment);
MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", caps.UniformBufferOffsetAlignment);
@@ -135,7 +135,7 @@ namespace MobileGL {
case TexturePixelDataType::UnsignedShort:
return TextureInternalFormat::RGBA16;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -148,7 +148,7 @@ namespace MobileGL {
case TexturePixelDataType::UnsignedByte:
return TextureInternalFormat::RGB8;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -163,7 +163,7 @@ namespace MobileGL {
case TexturePixelDataType::UnsignedShort:
return TextureInternalFormat::RG16;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -178,7 +178,7 @@ namespace MobileGL {
case TexturePixelDataType::UnsignedShort:
return TextureInternalFormat::R16;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -195,7 +195,7 @@ namespace MobileGL {
case TexturePixelDataType::Float:
return TextureInternalFormat::DepthComponent32F;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -208,7 +208,7 @@ namespace MobileGL {
case TexturePixelDataType::UnsignedInt248:
return TextureInternalFormat::Depth24Stencil8;
default:
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, "
"returning original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -217,7 +217,7 @@ namespace MobileGL {
}
}
default: {
MGLOG_W("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning "
MGLOG_W_ONCE("%s: Can't infer sized internal format from internalformat=%s, format=%s, type=%s, returning "
"original.",
__func__, MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str(),
MG_Util::ConvertTextureInputFormatToString(format).c_str(),
@@ -310,7 +310,7 @@ namespace MobileGL {
case TextureInternalFormat::DepthStencil:
return TextureInternalFormat::DepthStencil;
default:
MGLOG_W("%s: Unknown or unhandled internal format %s, returning original.", __func__,
MGLOG_W_ONCE("%s: Unknown or unhandled internal format %s, returning original.", __func__,
MG_Util::ConvertTextureInternalFormatToString(internalformat).c_str());
return internalformat;
}
@@ -28,7 +28,7 @@ namespace MobileGL {
// DrawArrays/DrawElements rewrite line loops into closed indexed
// strips; entry points without that rewrite (instanced/indirect)
// degrade to an open strip, which only misses the closing segment.
MGLOG_W("GL_LINE_LOOP without index rewrite; drawing as LINE_STRIP");
MGLOG_W_ONCE("GL_LINE_LOOP without index rewrite; drawing as LINE_STRIP");
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case GL_LINES_ADJACENCY:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
@@ -43,7 +43,7 @@ namespace MobileGL {
// state (patchControlPoints), not part of the topology.
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
MGLOG_W("Unrecognized primitive topology");
MGLOG_W_ONCE("Unrecognized primitive topology");
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
}
}
@@ -57,7 +57,7 @@ namespace MobileGL {
case GL_POINT:
return VK_POLYGON_MODE_POINT;
default:
MGLOG_W("Unrecognized polygon mode");
MGLOG_W_ONCE("Unrecognized polygon mode");
return VK_POLYGON_MODE_FILL;
}
}
@@ -73,7 +73,7 @@ namespace MobileGL {
case CullFaceMode::Unknown:
case CullFaceMode::CullFaceModeCount:
default:
MGLOG_W("Unrecognized cull face mode");
MGLOG_W_ONCE("Unrecognized cull face mode");
return VK_CULL_MODE_BACK_BIT;
}
}
+7 -1
View File
@@ -115,7 +115,13 @@ namespace MobileGL {
#endif
#if MOBILEGL_LOG_ENABLE_ANDROID && defined(__ANDROID__)
__android_log_print(androidLogLevel, "MobileGL", "%s", out.c_str());
// Without the trailing newline that the file sink needs: logcat terminates
// records itself, so handing it an already-newline-terminated string made
// every MobileGL log occupy TWO logcat records, the second one empty. That
// halved the useful depth of every `adb logcat -t N` window the CI
// diagnostics read (android-plugin/trace-replay-ci.sh).
__android_log_print(androidLogLevel, "MobileGL", "%.*s", static_cast<int>(out.size() - 1),
out.c_str());
#endif
WriteToFile(out.c_str());
+70 -3
View File
@@ -9,10 +9,24 @@
#pragma once
#include <Includes.h>
#include <atomic>
// Severity order, ascending. MOBILEGL_LOG_ACTIVE_LEVEL names the LOWEST severity that is
// compiled in, so every level at or above it survives and everything below it becomes a
// no-op: the production default INFO admits I/W/E/F and drops only D.
//
// This ordering was inverted until 2026-08-13 (DEBUG < WARN < ERROR < INFO < FATAL), which
// silently compiled MGLOG_W and MGLOG_E out of every production and CI build and cost
// several real diagnostic blackouts. Do not reorder without re-reading every
// `#if MOBILEGL_LOG_ACTIVE_LEVEL <= ...` in the tree.
//
// These five constants are duplicated verbatim in Defines.h, which needs them for the
// MOBILEGL_ASSERT gate in translation units that do not include Log.h. Keep both copies
// in sync; the values are load-bearing, not cosmetic.
#define MOBILEGL_LOG_LEVEL_DEBUG 0
#define MOBILEGL_LOG_LEVEL_WARN 1
#define MOBILEGL_LOG_LEVEL_ERROR 2
#define MOBILEGL_LOG_LEVEL_INFO 3
#define MOBILEGL_LOG_LEVEL_INFO 1
#define MOBILEGL_LOG_LEVEL_WARN 2
#define MOBILEGL_LOG_LEVEL_ERROR 3
#define MOBILEGL_LOG_LEVEL_FATAL 4
#define MOBILEGL_LOG_INTERNAL(levelTag, androidLogLevel, fmt, ...) \
@@ -20,6 +34,28 @@
MobileGL::MG_Util::Debug::Log(levelTag, androidLogLevel, fmt, ##__VA_ARGS__); \
} while (0)
// Emit `inner` at most once per call site, for the life of the process.
//
// Production logging is not allowed to repeat: a diagnostic on a per-draw or per-frame
// path costs frame time on every occurrence and buries the rest of the log. Anything at
// W or E that sits on such a path must either be latched with one of the _ONCE forms
// below or be demoted to MGLOG_D, which production compiles out entirely.
//
// The latch is a function-local atomic - zero-initialised before any dynamic
// initialisation runs, so it is safe from any thread at any time, needs no guard
// variable, and costs one relaxed test-and-set on the already-cold failure path. Note
// that the latch is per CALL SITE, not per subject: a site that reports "texture %u is
// unsupported" reports only the first such texture. That is the intended trade - the
// first occurrence is what a user shares for troubleshooting, and MGLOG_D still shows
// every occurrence in a dev build.
#define MOBILEGL_LOG_ONCE_INTERNAL(inner, fmt, ...) \
do { \
static ::std::atomic_flag mobileglLogOnceLatch; \
if (!mobileglLogOnceLatch.test_and_set(::std::memory_order_relaxed)) { \
inner(fmt, ##__VA_ARGS__); \
} \
} while (0)
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
#define MGLOG_D(fmt, ...) MOBILEGL_LOG_INTERNAL("DEBUG", ANDROID_LOG_DEBUG, fmt, ##__VA_ARGS__)
#else
@@ -55,6 +91,37 @@
{}
#endif
// One-shot forms. Each is gated on its own level so that a suppressed level leaves no
// latch behind - MGLOG_D_ONCE in a production build is nothing at all, not a byte of
// state plus a test-and-set.
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
#define MGLOG_D_ONCE(fmt, ...) MOBILEGL_LOG_ONCE_INTERNAL(MGLOG_D, fmt, ##__VA_ARGS__)
#else
#define MGLOG_D_ONCE(fmt, ...) \
{}
#endif
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_INFO
#define MGLOG_I_ONCE(fmt, ...) MOBILEGL_LOG_ONCE_INTERNAL(MGLOG_I, fmt, ##__VA_ARGS__)
#else
#define MGLOG_I_ONCE(fmt, ...) \
{}
#endif
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_WARN
#define MGLOG_W_ONCE(fmt, ...) MOBILEGL_LOG_ONCE_INTERNAL(MGLOG_W, fmt, ##__VA_ARGS__)
#else
#define MGLOG_W_ONCE(fmt, ...) \
{}
#endif
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_ERROR
#define MGLOG_E_ONCE(fmt, ...) MOBILEGL_LOG_ONCE_INTERNAL(MGLOG_E, fmt, ##__VA_ARGS__)
#else
#define MGLOG_E_ONCE(fmt, ...) \
{}
#endif
namespace MobileGL {
namespace MG_Util {
namespace Debug {
+1 -1
View File
@@ -517,7 +517,7 @@ namespace MobileGL {
// parameter queries on the initial state); every size stays 0.
break;
default:
MGLOG_W("Unimplemented internal format in GetComponentSizesForInternalFormat: %d",
MGLOG_W_ONCE("Unimplemented internal format in GetComponentSizesForInternalFormat: %d",
static_cast<Int>(internal));
break;
}
@@ -463,11 +463,10 @@ namespace MobileGL {
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR:
// MGLOG_I, deliberately: at the INFO compile level of every
// CI/WSL/retrace build, MGLOG_E and MGLOG_W are compiled out
// (Log.h orders DEBUG < WARN < ERROR < INFO) and the VUID
// would never reach a log.
MGLOG_I("[spirv] %s: %s (word index %zu)", site, text, position.index);
// Unlatched: only reachable with the validation switch armed,
// and every VUID names a different defect. (Parked at MGLOG_I
// until the Log.h ordering fix made E live at INFO.)
MGLOG_E("[spirv] %s: %s (word index %zu)", site, text, position.index);
break;
default:
MGLOG_D("[spirv] %s: %s", site, text);
@@ -486,7 +485,7 @@ namespace MobileGL {
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
tools.SetMessageConsumer(MakeSpirvMessageConsumer(site));
if (!tools.Validate(binary)) {
MGLOG_I("[spirv] %s: produced a module that fails validation (failure #%llu)",
MGLOG_E("[spirv] %s: produced a module that fails validation (failure #%llu)",
site,
static_cast<unsigned long long>(
ShaderCompiler::NoteSpirvValidationFailure()));
@@ -837,10 +836,10 @@ namespace MobileGL {
}
if (LegalizeFragmentOutputIndexPass::BinaryHasDynamicOutputIndexing(outputBinary)) {
// MGLOG_I, deliberately: MGLOG_E/W are compiled out at the INFO level every
// CI and retrace build uses, and this is precisely the diagnostic that has
// to survive to explain a shader the driver is about to reject.
MGLOG_I("[spirv] LegalizeFragmentOutputIndexingForEssl: a fragment output is still "
// MGLOG_W, latched: this runs per shader compile, and shader packs compile
// lazily mid-session, so an unlatched line here is unbounded runtime noise.
// (Parked at MGLOG_I until the Log.h ordering fix made W live at INFO.)
MGLOG_W_ONCE("[spirv] LegalizeFragmentOutputIndexingForEssl: a fragment output is still "
"indexed dynamically; a strict ES driver will reject this shader");
}
return true;
@@ -866,9 +865,8 @@ namespace MobileGL {
// access path there is no correct answer to substitute, because the ES texture
// genuinely has a height the GL one does not.
//
// MGLOG_I, deliberately: MGLOG_E/W are compiled out at the INFO level every CI,
// retrace and release build uses, and this is exactly the diagnostic that has to
// survive to explain the shader the driver is about to reject.
// MGLOG_W, latched: per shader compile, and shader packs compile lazily
// mid-session. (Parked at MGLOG_I until the Log.h ordering fix made W live.)
const auto traits = Lower1DArrayImagesPass::InspectBinary(inputBinary);
// The overwhelmingly common answer, and the reason the inspection exists: no
// 1D-array storage image, so the module is handed back byte for byte without an
@@ -879,7 +877,7 @@ namespace MobileGL {
return true;
}
if (traits.queriesImageSize) {
MGLOG_I("[spirv] Lower1DArrayImagesForEssl: the module queries the size of a 1D-array "
MGLOG_W_ONCE("[spirv] Lower1DArrayImagesForEssl: the module queries the size of a 1D-array "
"storage image, which cannot be answered in the 2D-array shape ES stores it in; "
"leaving the module alone, and a strict ES driver will reject it");
outputBinary = inputBinary;
@@ -1275,7 +1275,7 @@ namespace MobileGL {
// matches (e.g. the pack shipped a new shader revision), the affected device
// silently falls back to the driver's miscompiled path. Make that visible.
if (CountToken(tokens, "subgroupInclusiveAdd") > 0) {
MGLOG_W("%s: subgroupInclusiveAdd present but the linear prefix-scan template "
MGLOG_W_ONCE("%s: subgroupInclusiveAdd present but the linear prefix-scan template "
"did not match; the wide-subgroup rewrite was NOT applied",
__func__);
}
@@ -1357,7 +1357,7 @@ namespace MobileGL {
continue;
}
if (quirk.Apply(quirkContext, source)) {
MGLOG_I("ApplyShaderSourceQuirks: applied '%s'%s", quirk.name,
MGLOG_D("ApplyShaderSourceQuirks: applied '%s'%s", quirk.name,
quirkOverride == MG_Config::QuirkOverride::ForceOn ? " (forced on)" : "");
}
}
@@ -203,7 +203,7 @@ namespace MobileGL {
static_cast<spv::ExecutionModel>(entryPoint->GetSingleWordInOperand(0));
if (executionModel == spv::ExecutionModel::Geometry ||
executionModel == spv::ExecutionModel::TessellationControl) {
MGLOG_I("FlattenXfbInterfaceBlocksPass: execution model %u publishes outputs outside "
MGLOG_D("FlattenXfbInterfaceBlocksPass: execution model %u publishes outputs outside "
"the entry point's return; leaving its blocks declared as blocks",
static_cast<Uint32>(executionModel));
return Status::SuccessWithoutChange;
@@ -302,7 +302,7 @@ namespace MobileGL {
target.members.push_back(member);
}
if (!usable || target.members.empty()) {
MGLOG_I("FlattenXfbInterfaceBlocksPass: block '%s' has a member this pass cannot "
MGLOG_D("FlattenXfbInterfaceBlocksPass: block '%s' has a member this pass cannot "
"place; leaving it declared as a block",
blockName.c_str());
continue;
@@ -370,7 +370,7 @@ namespace MobileGL {
continue;
}
if (derivedPointers.count(operand.words[0]) == 0) continue;
MGLOG_I("FlattenXfbInterfaceBlocksPass: interface block %%%u reaches a "
MGLOG_D("FlattenXfbInterfaceBlocksPass: interface block %%%u reaches a "
"SPIR-V opcode %u that this pass cannot follow; leaving it "
"declared as a block",
operand.words[0], static_cast<Uint32>(opcode));
@@ -82,7 +82,7 @@ namespace MobileGL {
// 6 or 8 uint32 components has no single vertex format, and GL spreads such
// an input over two attribute locations. Left alone; the vertex-input
// factory declines the matching attribute for the same reason.
MGLOG_E("PackDoubleVertexInputsPass: vertex input %%%u is a %u-component 64-bit "
MGLOG_E_ONCE("PackDoubleVertexInputsPass: vertex input %%%u is a %u-component 64-bit "
"float; only double and dvec2 inputs can be packed",
inst.result_id(), components);
continue;
@@ -128,7 +128,7 @@ namespace MobileGL {
const Uint32 elementTypeId = typeMgr->GetId(arrayType->element_type());
const Uint32 locationsPerElement = LocationsPerElement(arrayType->element_type());
if (elementTypeId == 0 || locationsPerElement == 0) {
MGLOG_I("SplitArrayVertexInputsPass: vertex input %%%u is an array whose element "
MGLOG_D("SplitArrayVertexInputsPass: vertex input %%%u is an array whose element "
"type has no single-location mapping; leaving it declared as an array",
inst.result_id());
continue;
@@ -195,7 +195,7 @@ namespace MobileGL {
continue;
}
if (derivedPointers.count(operand.words[0]) == 0) continue;
MGLOG_I("SplitArrayVertexInputsPass: array vertex input %%%u reaches a "
MGLOG_D("SplitArrayVertexInputsPass: array vertex input %%%u reaches a "
"SPIR-V opcode %u that this pass cannot follow; leaving it "
"declared as an "
"array",
@@ -95,7 +95,7 @@ namespace MobileGL {
if (member.array.dims_count > 1) {
// Arrays of arrays of structs cannot be declared in the GL 3.3-era GLSL
// MobileGL ingests; record the base so at least element 0 resolves.
MGLOG_W("FlattenGlobalUboMember: multi-dimensional struct array '%s' is not supported, "
MGLOG_W_ONCE("FlattenGlobalUboMember: multi-dimensional struct array '%s' is not supported, "
"flattening element 0 only",
name.c_str());
}
@@ -455,7 +455,7 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
break;
default:
MGLOG_E("NormalizePixelFormat: outFormat: unhandled internalFormat: %s",
MGLOG_E_ONCE("NormalizePixelFormat: outFormat: unhandled internalFormat: %s",
MG_Util::ConvertGLEnumToString(internalFormat).c_str());
// Fallback handling for other formats
// Try to infer format from internal format name
@@ -676,7 +676,7 @@ namespace MobileGL::MG_Util::TextureFormatProcessor {
break;
default:
MGLOG_E("NormalizePixelFormat: outType: unhandled internalFormat: %s",
MGLOG_E_ONCE("NormalizePixelFormat: outType: unhandled internalFormat: %s",
MG_Util::ConvertGLEnumToString(internalFormat).c_str());
// Fallback handling for other formats
*outType = GL_UNSIGNED_BYTE;
+5 -1
View File
@@ -187,7 +187,11 @@ collect_run_diagnostics() {
if [ "${adb_state}" != "device" ]; then
return
fi
"${ADB}" logcat -d -t 2000 > "${diagnostics_dir}/logcat.txt" || true
# Depth matters, not just content: is_infrastructure_failure below decides "the emulator
# broke, retry" by finding a system_server crash in this window, and MobileGL logs into the
# same buffer under its own tag. A tail that is too short lets routine MobileGL output evict
# the crash line and charges an infrastructure fault to the trace under test.
"${ADB}" logcat -d -t 20000 > "${diagnostics_dir}/logcat.txt" || true
adb_device_path shell pidof "${package_name}" > "${diagnostics_dir}/pidof.txt" 2>&1 || true
adb_device_path shell dumpsys activity activities > "${diagnostics_dir}/activity.txt" 2>&1 || true
adb_device_path shell run-as "${package_name}" ls -laR "${app_dir}" > "${diagnostics_dir}/app-files.txt" 2>&1 || true