[Refactor] (CMakeLists.txt, MG_Backend, MG_Backend/DirectVulkan, MG_Impl/GLImpl, MG_Impl/Pipe, MG_IntegrationTest/Scenarios, MG_Pipe/generated, MG_Remote/Client, MG_Test/Query, MG_Test/Texture, scripts, scripts/data): move the function table and shared-image types out of BackendObject.h

- BackendFunctions.h holds GLFunctionsTable, GlobalBackendFunctionsTable, CopyImageEndpoint, the sync and query handles and the present damage; files that only call through the table include it instead of the backend object
- SharedImageTypes.h holds the shared-image structs
- FormatCapabilities.cpp holds the BackendCaps.h helpers that BackendObject.cpp defined
- gen_pipe.py reads the table from its new header
- two comments that cited a BackendObject.cpp line name the function instead
This commit is contained in:
2026-10-16 23:30:20 -04:00
parent ad7340921e
commit 55fa9cdfdc
19 changed files with 501 additions and 451 deletions
+2
View File
@@ -496,6 +496,8 @@ set(SOURCE_FILES
MobileGL/MG_Backend/Init.cpp
MobileGL/MG_Backend/BackendObject.cpp
MobileGL/MG_Backend/BackendFunctions.cpp
MobileGL/MG_Backend/FormatCapabilities.cpp
MobileGL/MG_Backend/ClientReadback.cpp
MobileGL/MG_Backend/GpuHangWatch.cpp
+25
View File
@@ -0,0 +1,25 @@
// MobileGL - MobileGL/MG_Backend/BackendFunctions.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
#include "BackendFunctions.h"
namespace MobileGL::MG_Backend {
namespace {
MG_Util::Damage::Region& PresentDamageSlot() {
thread_local MG_Util::Damage::Region damage = MG_Util::Damage::Region::Full();
return damage;
}
} // namespace
const MG_Util::Damage::Region& CurrentPresentDamage() {
return PresentDamageSlot();
}
void SetCurrentPresentDamage(const MG_Util::Damage::Region& damage) {
PresentDamageSlot() = damage;
}
} // namespace MobileGL::MG_Backend
+244
View File
@@ -0,0 +1,244 @@
// MobileGL - MobileGL/MG_Backend/BackendFunctions.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#pragma once
#include <Includes.h>
#include "MG_State/GLState/TextureState/TextureEnum.h"
#include <MG_Util/Damage/Damage.h>
#include <MG_Pipe/MGPipeHandles.h>
namespace MobileGL::MG_State::GLState {
class FramebufferObject;
class ITextureObject;
class RenderbufferObject;
} // namespace MobileGL::MG_State::GLState
namespace MobileGL::MG_Backend {
// One endpoint of a glCopyImageSubData. GL 4.6 core 18.3.2 accepts GL_RENDERBUFFER
// alongside the ten whole-image texture targets, and a renderbuffer name lives in a
// namespace of its own - so an endpoint is a sum type, not an ITextureObject. At most
// one of the two pointers is set; neither is set when the name named nothing, which is
// the INVALID_VALUE the frontend validator reports.
struct CopyImageEndpoint {
SharedPtr<MG_State::GLState::ITextureObject> Texture;
SharedPtr<MG_State::GLState::RenderbufferObject> Renderbuffer;
MG_Pipe::MGPipeHandle TextureHandle = MG_Pipe::kMGPipeNullHandle;
MG_Pipe::MGPipeHandle RenderbufferHandle = MG_Pipe::kMGPipeNullHandle;
Bool IsRenderbuffer() const {
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
return Renderbuffer != nullptr;
}
Bool Exists() const {
if (!MG_Pipe::MGPipeHandleIsNull(TextureHandle)) return true;
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
return Texture != nullptr || Renderbuffer != nullptr;
}
};
// Opaque backend fence-sync handle, created by GLFunctionsTable::FenceSync
// and released by GLFunctionsTable::DeleteSync.
using BackendSyncHandle = void*;
// Opaque backend timer-query handle, created by
// GLFunctionsTable::BeginTimeElapsedQuery / QueryCounterTimestamp and
// released by GLFunctionsTable::DeleteBackendQuery.
using BackendQueryHandle = void*;
struct GLFunctionsTable {
void (*DrawArrays)(GLenum mode, GLint first, GLsizei count);
void (*DrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices);
void (*DrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
void (*MultiDrawArrays)(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
void (*MultiDrawElements)(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount);
void (*MultiDrawElementsBaseVertex)(GLenum mode, const GLsizei* count, GLenum type,
const GLvoid* const* indices, GLsizei drawcount, const GLint* basevertex);
void (*MultiDrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount,
GLsizei stride);
void (*MultiDrawArraysIndirect)(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void (*MultiDrawElementsIndirectCount)(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void (*MultiDrawArraysIndirectCount)(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void (*DrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void (*DrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices);
void (*DrawElementsInstancedBaseVertexBaseInstance)(GLenum mode, GLsizei count, GLenum type,
const void* indices, GLsizei instancecount,
GLint basevertex, GLuint baseinstance);
void (*DrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex);
void (*DrawElementsInstancedBaseInstance)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance);
void (*DrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount);
void (*DrawArraysInstancedBaseInstance)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance);
void (*DrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void (*DrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect);
void (*DrawArraysIndirect)(GLenum mode, const void* indirect);
void (*Clear)(GLbitfield mask);
void (*ClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void (*ClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint* value);
void (*ClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint* value);
void (*ClearNamedFramebufferfv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearNamedFramebufferfi)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void (*ClearNamedFramebufferiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLint* value);
void (*ClearNamedFramebufferuiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLuint* value);
void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (*BlitNamedFramebuffer)(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (*CopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border);
void (*CopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
GLsizei width, GLsizei height);
// The by-name form: the destination is `texture`, whatever the units hold. `target` is
// the one the record carries (the texture's own 2D-class target).
void (*CopyTextureSubImage2D)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target,
GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height);
void (*CopyImageSubData)(const CopyImageEndpoint& src, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY,
GLint srcZ, const CopyImageEndpoint& dst, GLenum dstTarget, GLint dstLevel, GLint dstX,
GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
void (*GenerateMipmap)(GLenum target);
// The by-name form, also used once the bound-target form has resolved its texture.
void (*GenerateTextureMipmap)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target);
void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
void (*GetTexImage)(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void (*GetTextureImage)(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type,
GLsizei bufSize, GLvoid* pixels);
void (*DispatchCompute)(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void (*DispatchComputeIndirect)(GLintptr indirect);
void (*MemoryBarrier)(GLbitfield barriers);
void (*MemoryBarrierByRegion)(GLbitfield barriers);
void (*BindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer,
GLenum access, GLenum format);
// The ONLY indexed query that is genuinely a backend one, and only for the pnames
// MG_Impl/GLImpl/Getter/GL_Getter.cpp does not already own. Every indexed pname that
// names FRONTEND state - the indexed buffer bindings, the per-unit texture/sampler
// bindings, the image-unit bindings, the viewport rectangles, the indexed capabilities
// - is answered in GL_Getter::GetIntegeri_v and never reaches this entry; the
// 64-bit and float/double widths are derived there from the same answer, which is why
// no GetInteger64i_v/GetFloati_v/GetDoublei_v table entry exists. In practice this
// leaves GL_MAX_COMPUTE_WORK_GROUP_COUNT / _SIZE (also asked directly by
// MG_Util/ShaderTranspiler/CompileEnv.cpp) plus whatever pname the frontend has no
// case for at all.
void (*GetIntegeri_v)(GLenum target, GLuint index, GLint* data);
// There is deliberately NO GetProgramiv entry: glGetProgramiv describes the program
// the APPLICATION wrote - link status, the transform-feedback mode, the compute local
// size - all of which are frontend link artifacts on ProgramObject, and
// MG_Impl/GLImpl/Program/GL_Program.cpp answers every one of them from there. Asking a
// backend would mean asking about a DIFFERENT program (a SPIRV-Cross-generated ESSL
// one, or a SPIR-V module), in a namespace the application never sees.
// The GL program interface (glGetProgramInterfaceiv / glGetProgramResource*) is NOT
// a backend query: it describes the program the application wrote, in the
// application's namespace, which neither backend program is in. It is answered
// entirely by MG_Impl/GLImpl/Program/ProgramInterface from the frontend reflection.
// Takes the block's GL NAME, not glShaderStorageBlockBinding's index. The index
// the application passes is the frontend interface-query enumeration's, and no
// backend shares that index space: DirectVulkan enumerates SPIR-V descriptor
// bindings and DirectGLES asks a real driver about SPIRV-Cross-generated ESSL.
// The name is the one coordinate all three agree on, so the frontend resolves the
// index against its own enumeration and each backend maps the name to its own.
void (*ShaderStorageBlockBinding)(GLuint program, const GLchar* storageBlockName, GLuint storageBlockBinding);
// GL fence sync objects. All entries are optional (may be null); the
// frontend then falls back to always-signaled sync semantics.
// FenceSync may itself return null when the backend cannot create a
// fence right now (e.g. the calling thread does not own the backend
// context); the frontend treats such a sync as always signaled.
BackendSyncHandle (*FenceSync)();
GLenum (*ClientWaitSync)(BackendSyncHandle sync, GLbitfield flags, GLuint64 timeout);
void (*WaitSync)(BackendSyncHandle sync, GLbitfield flags, GLuint64 timeout);
void (*DeleteSync)(BackendSyncHandle sync);
Bool (*GetSyncStatus)(BackendSyncHandle sync); // true = signaled
// GL timer-query objects (GL_ARB_timer_query). All entries are
// optional (may be null); the frontend then falls back to zero
// results and reports GL_QUERY_COUNTER_BITS == 0.
// BeginTimeElapsedQuery / QueryCounterTimestamp may themselves
// return null when the backend cannot create a query right now;
// the frontend treats such a query as immediately available with
// a zero result.
// Dynamic support check: true only when the live backend can
// actually time at the moment of the call (extension / entry
// points / timestamp valid bits are known then, not at table
// init). Gates the advertised GL_QUERY_COUNTER_BITS.
Bool (*IsTimerQuerySupported)();
BackendQueryHandle (*BeginTimeElapsedQuery)(); // starts a TIME_ELAPSED span
void (*EndTimeElapsedQuery)(BackendQueryHandle query); // ends the span
BackendQueryHandle (*QueryCounterTimestamp)(); // glQueryCounter(GL_TIMESTAMP) one-shot
Bool (*IsQueryResultAvailable)(BackendQueryHandle query); // non-blocking
// Returns true when a final value was produced (*outNanoseconds
// written; the frontend may cache it and release the handle).
// Returns false when the result could not be obtained YET - e.g.
// a Vulkan wait that refuses to block on a not-yet-submitted
// frame serial - in which case the frontend must keep the handle
// and leave the query readable later.
Bool (*GetQueryResult64)(BackendQueryHandle query, Bool wait, Uint64* outNanoseconds);
void (*DeleteBackendQuery)(BackendQueryHandle query);
// GL_SAMPLES_PASSED occlusion queries (optional; null = unsupported,
// the frontend then rejects the target). Results/deletion flow through
// GetQueryResult64 / DeleteBackendQuery like timer queries.
BackendQueryHandle (*BeginOcclusionQuery)();
void (*EndOcclusionQuery)(BackendQueryHandle query);
// Transform feedback primitive queries backed by real GPU query pools
// (optional; null = frontend falls back to CPU accounting).
BackendQueryHandle (*BeginXfbPrimitivesQuery)(Bool generated);
void (*EndXfbPrimitivesQuery)(BackendQueryHandle query);
// Whether GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN should be answered from the
// frontend's own accounting wherever that accounting is exact - a capture with no
// geometry stage - instead of from the query above. Set by DirectGLES, whose result
// is whatever the ES driver's PRIMITIVES_WRITTEN counter says: Adreno reports twice
// the written count for a vertex-only capture that follows a large render pass,
// where the desktop-exact answer is the one the frontend already computed. Defaults
// to false, so a backend that never sets it keeps using its GPU result.
Bool PrefersCpuXfbPrimitiveAccounting = false;
// Transform feedback capture spans, for backends whose own GL/ES driver
// performs the capture (DirectGLES). Both optional; null means the backend
// drives capture from its draw recording instead (DirectVulkan). End is
// called while the frontend capture state is still active, so the backend
// can still see the capture program and buffer bindings.
// GL_PATCH_VERTICES; ES 3.2 spells it the same way.
void (*PatchParameteri)(GLenum pname, GLint value);
void (*BeginTransformFeedback)(GLenum primitiveMode);
void (*EndTransformFeedback)();
// ARB_transform_feedback2. A backend that leaves these null keeps the single
// implicit capture span the frontend has always modelled; the frontend state
// (paused flag, per-object bindings) is tracked either way.
void (*PauseTransformFeedback)();
void (*ResumeTransformFeedback)();
void (*BindTransformFeedback)(GLuint name);
void (*DeleteTransformFeedback)(GLuint name);
Int64 (*GetGpuTimestampNs)(); // glGetInteger64v(GL_TIMESTAMP); 0 if unsupported
};
struct GlobalBackendFunctionsTable {
GLFunctionsTable GL;
void (*Present)();
// Optional: applies the app-requested eglSwapInterval to the native
// presentation path (null = backend keeps its own pacing policy).
void (*SetSwapInterval)(Int interval);
};
// THE DAMAGE OF THE PRESENT BEING APPLIED on this thread: the server's apply thread sets it
// around the backend's Present (from the present record), and a backend that can hand damage
// to its native present reads it there. Full whenever nothing set it.
const MG_Util::Damage::Region& CurrentPresentDamage();
void SetCurrentPresentDamage(const MG_Util::Damage::Region& damage);
extern GlobalBackendFunctionsTable gBackendFunctionsTable;
} // namespace MobileGL::MG_Backend
-156
View File
@@ -7,11 +7,7 @@
// End of Source File Header
#include "BackendObject.h"
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
namespace MobileGL::MG_Backend {
namespace {
@@ -23,146 +19,8 @@ namespace MobileGL::MG_Backend {
std::thread::id CurrentThreadKey() {
return std::this_thread::get_id();
}
const char* GetFormatCapabilitySupportString(const FormatCapabilityCache& cache, SizeT targetIndex,
SizeT formatIndex, FormatCapability capability) {
if (HasFormatCapability(cache.FullCaps[targetIndex][formatIndex], capability)) return "Full";
if (HasFormatCapability(cache.CaveatCaps[targetIndex][formatIndex], capability)) return "Caveat";
return "None";
}
SizeT GetPrintedFormatNameWidth() {
SizeT width = 0;
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const auto format = static_cast<TextureInternalFormat>(formatIndex);
width = std::max(width, MG_Util::ConvertTextureInternalFormatToString(format).size());
}
return width;
}
SizeT GetCapabilityColumnWidth(FormatCapability capability) {
SizeT width = std::strlen(GetFormatCapabilityName(capability));
width = std::max<SizeT>(width, std::strlen("Caveat"));
return width;
}
String BuildFormatCapabilityHeader(SizeT formatNameWidth) {
std::ostringstream line;
line << std::left << std::setw(static_cast<Int>(formatNameWidth)) << "";
for (FormatCapability capability : kReportedFormatCapabilities) {
line << " | " << std::left << std::setw(static_cast<Int>(GetCapabilityColumnWidth(capability)))
<< GetFormatCapabilityName(capability);
}
return line.str();
}
String BuildFormatCapabilityRow(const FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
SizeT formatNameWidth) {
const auto format = static_cast<TextureInternalFormat>(formatIndex);
std::ostringstream line;
line << std::left << std::setw(static_cast<Int>(formatNameWidth))
<< MG_Util::ConvertTextureInternalFormatToString(format);
for (FormatCapability capability : kReportedFormatCapabilities) {
line << " | " << std::left << std::setw(static_cast<Int>(GetCapabilityColumnWidth(capability)))
<< GetFormatCapabilitySupportString(cache, targetIndex, formatIndex, capability);
}
return line.str();
}
} // namespace
void FormatCapabilityCache::Clear() {
for (auto& row : FullCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : CaveatCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : SampleCounts) {
for (auto& counts : row) {
counts.clear();
}
}
}
Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability) {
return static_cast<Bool>(caps & capability);
}
SizeT GetFormatCapabilityTargetIndex(TextureTarget target) {
if (target == TextureTarget::Unknown || static_cast<Int>(target) < 0 ||
static_cast<SizeT>(target) >= kFormatCapabilityTextureTargetCount) {
return kFormatCapabilityTargetCount;
}
// An external texture is a 2D one wherever a format can go.
if (target == TextureTarget::External) return static_cast<SizeT>(TextureTarget::Texture2D);
return static_cast<SizeT>(target);
}
SizeT GetRenderbufferFormatCapabilityTargetIndex() {
return kFormatCapabilityRenderbufferTargetIndex;
}
const char* GetFormatCapabilityName(FormatCapability capability) {
switch (capability) {
case FormatCapability::Creatable:
return "Creatable";
case FormatCapability::Sampled:
return "Sampled";
case FormatCapability::LinearFilter:
return "LinearFilter";
case FormatCapability::GenerateMipmap:
return "GenerateMipmap";
case FormatCapability::TextureGather:
return "TextureGather";
case FormatCapability::TextureShadow:
return "TextureShadow";
case FormatCapability::FramebufferRenderable:
return "FramebufferRenderable";
case FormatCapability::FramebufferLayered:
return "FramebufferLayered";
case FormatCapability::MultisampleTexture:
return "MultisampleTexture";
case FormatCapability::MultisampleRenderbuffer:
return "MultisampleRenderbuffer";
case FormatCapability::ColorAttachment:
return "ColorAttachment";
case FormatCapability::DepthAttachment:
return "DepthAttachment";
case FormatCapability::StencilAttachment:
return "StencilAttachment";
case FormatCapability::TextureBuffer:
return "TextureBuffer";
}
return "Unknown";
}
String GetFormatCapabilityTargetName(SizeT targetIndex) {
if (targetIndex == kFormatCapabilityRenderbufferTargetIndex) {
return "Renderbuffer";
}
if (targetIndex >= kFormatCapabilityTextureTargetCount) {
return "Unknown";
}
return MG_Util::ConvertTextureTargetToString(static_cast<TextureTarget>(targetIndex));
}
void PrintFormatCapabilities(const FormatCapabilityCache& cache) {
const SizeT formatNameWidth = GetPrintedFormatNameWidth();
MGLOG_D("Backend format capabilities:");
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTargetCount; ++targetIndex) {
MGLOG_D("");
const String targetName = GetFormatCapabilityTargetName(targetIndex);
MGLOG_D("- %s", targetName.c_str());
const String header = BuildFormatCapabilityHeader(formatNameWidth);
MGLOG_D("%s", header.c_str());
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const String row = BuildFormatCapabilityRow(cache, targetIndex, formatIndex, formatNameWidth);
MGLOG_D("%s", row.c_str());
}
}
}
Bool BackendObject::InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (dpy == EGL_NO_DISPLAY) {
@@ -386,20 +244,6 @@ namespace MobileGL::MG_Backend {
m_windowHandle = {};
}
namespace {
MG_Util::Damage::Region& PresentDamageSlot() {
thread_local MG_Util::Damage::Region damage = MG_Util::Damage::Region::Full();
return damage;
}
} // namespace
const MG_Util::Damage::Region& CurrentPresentDamage() {
return PresentDamageSlot();
}
void SetCurrentPresentDamage(const MG_Util::Damage::Region& damage) {
PresentDamageSlot() = damage;
}
Bool BackendObject::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (!m_eglDisplayInitialized || m_eglDisplay != dpy) {
+2 -281
View File
@@ -9,238 +9,12 @@
#pragma once
#include <Includes.h>
#include <BackendCaps.h>
#include "MG_State/GLState/TextureState/TextureEnum.h"
#include <MG_Backend/BackendFunctions.h>
#include <MG_Backend/SharedImageTypes.h>
#include <MG_Util/Damage/Damage.h>
#include <MG_Pipe/MGPipeHandles.h>
namespace MobileGL {
namespace MG_State::GLState {
class FramebufferObject;
class ITextureObject;
class RenderbufferObject;
} // namespace MG_State::GLState
namespace MG_Backend {
// One endpoint of a glCopyImageSubData. GL 4.6 core 18.3.2 accepts GL_RENDERBUFFER
// alongside the ten whole-image texture targets, and a renderbuffer name lives in a
// namespace of its own - so an endpoint is a sum type, not an ITextureObject. At most
// one of the two pointers is set; neither is set when the name named nothing, which is
// the INVALID_VALUE the frontend validator reports.
struct CopyImageEndpoint {
SharedPtr<MG_State::GLState::ITextureObject> Texture;
SharedPtr<MG_State::GLState::RenderbufferObject> Renderbuffer;
MG_Pipe::MGPipeHandle TextureHandle = MG_Pipe::kMGPipeNullHandle;
MG_Pipe::MGPipeHandle RenderbufferHandle = MG_Pipe::kMGPipeNullHandle;
Bool IsRenderbuffer() const {
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
return Renderbuffer != nullptr;
}
Bool Exists() const {
if (!MG_Pipe::MGPipeHandleIsNull(TextureHandle)) return true;
if (!MG_Pipe::MGPipeHandleIsNull(RenderbufferHandle)) return true;
return Texture != nullptr || Renderbuffer != nullptr;
}
};
// Opaque backend fence-sync handle, created by GLFunctionsTable::FenceSync
// and released by GLFunctionsTable::DeleteSync.
using BackendSyncHandle = void*;
// Opaque backend timer-query handle, created by
// GLFunctionsTable::BeginTimeElapsedQuery / QueryCounterTimestamp and
// released by GLFunctionsTable::DeleteBackendQuery.
using BackendQueryHandle = void*;
struct GLFunctionsTable {
void (*DrawArrays)(GLenum mode, GLint first, GLsizei count);
void (*DrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices);
void (*DrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLint basevertex);
void (*MultiDrawArrays)(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
void (*MultiDrawElements)(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount);
void (*MultiDrawElementsBaseVertex)(GLenum mode, const GLsizei* count, GLenum type,
const GLvoid* const* indices, GLsizei drawcount,
const GLint* basevertex);
void (*MultiDrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount,
GLsizei stride);
void (*MultiDrawArraysIndirect)(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void (*MultiDrawElementsIndirectCount)(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void (*MultiDrawArraysIndirectCount)(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void (*DrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void (*DrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices);
void (*DrawElementsInstancedBaseVertexBaseInstance)(GLenum mode, GLsizei count, GLenum type,
const void* indices, GLsizei instancecount,
GLint basevertex, GLuint baseinstance);
void (*DrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex);
void (*DrawElementsInstancedBaseInstance)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance);
void (*DrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount);
void (*DrawArraysInstancedBaseInstance)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance);
void (*DrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void (*DrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect);
void (*DrawArraysIndirect)(GLenum mode, const void* indirect);
void (*Clear)(GLbitfield mask);
void (*ClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void (*ClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint* value);
void (*ClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint* value);
void (*ClearNamedFramebufferfv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearNamedFramebufferfi)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void (*ClearNamedFramebufferiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLint* value);
void (*ClearNamedFramebufferuiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLuint* value);
void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (*BlitNamedFramebuffer)(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (*CopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border);
void (*CopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
GLsizei width, GLsizei height);
// The by-name form: the destination is `texture`, whatever the units hold. `target` is
// the one the record carries (the texture's own 2D-class target).
void (*CopyTextureSubImage2D)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target,
GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height);
void (*CopyImageSubData)(const CopyImageEndpoint& src, GLenum srcTarget, GLint srcLevel, GLint srcX,
GLint srcY, GLint srcZ, const CopyImageEndpoint& dst, GLenum dstTarget,
GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth,
GLsizei srcHeight, GLsizei srcDepth);
void (*GenerateMipmap)(GLenum target);
// The by-name form, also used once the bound-target form has resolved its texture.
void (*GenerateTextureMipmap)(const SharedPtr<MG_State::GLState::ITextureObject>& texture, GLenum target);
void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels);
void (*GetTexImage)(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void (*GetTextureImage)(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type,
GLsizei bufSize, GLvoid* pixels);
void (*DispatchCompute)(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void (*DispatchComputeIndirect)(GLintptr indirect);
void (*MemoryBarrier)(GLbitfield barriers);
void (*MemoryBarrierByRegion)(GLbitfield barriers);
void (*BindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer,
GLenum access, GLenum format);
// The ONLY indexed query that is genuinely a backend one, and only for the pnames
// MG_Impl/GLImpl/Getter/GL_Getter.cpp does not already own. Every indexed pname that
// names FRONTEND state - the indexed buffer bindings, the per-unit texture/sampler
// bindings, the image-unit bindings, the viewport rectangles, the indexed capabilities
// - is answered in GL_Getter::GetIntegeri_v and never reaches this entry; the
// 64-bit and float/double widths are derived there from the same answer, which is why
// no GetInteger64i_v/GetFloati_v/GetDoublei_v table entry exists. In practice this
// leaves GL_MAX_COMPUTE_WORK_GROUP_COUNT / _SIZE (also asked directly by
// MG_Util/ShaderTranspiler/CompileEnv.cpp) plus whatever pname the frontend has no
// case for at all.
void (*GetIntegeri_v)(GLenum target, GLuint index, GLint* data);
// There is deliberately NO GetProgramiv entry: glGetProgramiv describes the program
// the APPLICATION wrote - link status, the transform-feedback mode, the compute local
// size - all of which are frontend link artifacts on ProgramObject, and
// MG_Impl/GLImpl/Program/GL_Program.cpp answers every one of them from there. Asking a
// backend would mean asking about a DIFFERENT program (a SPIRV-Cross-generated ESSL
// one, or a SPIR-V module), in a namespace the application never sees.
// The GL program interface (glGetProgramInterfaceiv / glGetProgramResource*) is NOT
// a backend query: it describes the program the application wrote, in the
// application's namespace, which neither backend program is in. It is answered
// entirely by MG_Impl/GLImpl/Program/ProgramInterface from the frontend reflection.
// Takes the block's GL NAME, not glShaderStorageBlockBinding's index. The index
// the application passes is the frontend interface-query enumeration's, and no
// backend shares that index space: DirectVulkan enumerates SPIR-V descriptor
// bindings and DirectGLES asks a real driver about SPIRV-Cross-generated ESSL.
// The name is the one coordinate all three agree on, so the frontend resolves the
// index against its own enumeration and each backend maps the name to its own.
void (*ShaderStorageBlockBinding)(GLuint program, const GLchar* storageBlockName,
GLuint storageBlockBinding);
// GL fence sync objects. All entries are optional (may be null); the
// frontend then falls back to always-signaled sync semantics.
// FenceSync may itself return null when the backend cannot create a
// fence right now (e.g. the calling thread does not own the backend
// context); the frontend treats such a sync as always signaled.
BackendSyncHandle (*FenceSync)();
GLenum (*ClientWaitSync)(BackendSyncHandle sync, GLbitfield flags, GLuint64 timeout);
void (*WaitSync)(BackendSyncHandle sync, GLbitfield flags, GLuint64 timeout);
void (*DeleteSync)(BackendSyncHandle sync);
Bool (*GetSyncStatus)(BackendSyncHandle sync); // true = signaled
// GL timer-query objects (GL_ARB_timer_query). All entries are
// optional (may be null); the frontend then falls back to zero
// results and reports GL_QUERY_COUNTER_BITS == 0.
// BeginTimeElapsedQuery / QueryCounterTimestamp may themselves
// return null when the backend cannot create a query right now;
// the frontend treats such a query as immediately available with
// a zero result.
// Dynamic support check: true only when the live backend can
// actually time at the moment of the call (extension / entry
// points / timestamp valid bits are known then, not at table
// init). Gates the advertised GL_QUERY_COUNTER_BITS.
Bool (*IsTimerQuerySupported)();
BackendQueryHandle (*BeginTimeElapsedQuery)(); // starts a TIME_ELAPSED span
void (*EndTimeElapsedQuery)(BackendQueryHandle query); // ends the span
BackendQueryHandle (*QueryCounterTimestamp)(); // glQueryCounter(GL_TIMESTAMP) one-shot
Bool (*IsQueryResultAvailable)(BackendQueryHandle query); // non-blocking
// Returns true when a final value was produced (*outNanoseconds
// written; the frontend may cache it and release the handle).
// Returns false when the result could not be obtained YET - e.g.
// a Vulkan wait that refuses to block on a not-yet-submitted
// frame serial - in which case the frontend must keep the handle
// and leave the query readable later.
Bool (*GetQueryResult64)(BackendQueryHandle query, Bool wait, Uint64* outNanoseconds);
void (*DeleteBackendQuery)(BackendQueryHandle query);
// GL_SAMPLES_PASSED occlusion queries (optional; null = unsupported,
// the frontend then rejects the target). Results/deletion flow through
// GetQueryResult64 / DeleteBackendQuery like timer queries.
BackendQueryHandle (*BeginOcclusionQuery)();
void (*EndOcclusionQuery)(BackendQueryHandle query);
// Transform feedback primitive queries backed by real GPU query pools
// (optional; null = frontend falls back to CPU accounting).
BackendQueryHandle (*BeginXfbPrimitivesQuery)(Bool generated);
void (*EndXfbPrimitivesQuery)(BackendQueryHandle query);
// Whether GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN should be answered from the
// frontend's own accounting wherever that accounting is exact - a capture with no
// geometry stage - instead of from the query above. Set by DirectGLES, whose result
// is whatever the ES driver's PRIMITIVES_WRITTEN counter says: Adreno reports twice
// the written count for a vertex-only capture that follows a large render pass,
// where the desktop-exact answer is the one the frontend already computed. Defaults
// to false, so a backend that never sets it keeps using its GPU result.
Bool PrefersCpuXfbPrimitiveAccounting = false;
// Transform feedback capture spans, for backends whose own GL/ES driver
// performs the capture (DirectGLES). Both optional; null means the backend
// drives capture from its draw recording instead (DirectVulkan). End is
// called while the frontend capture state is still active, so the backend
// can still see the capture program and buffer bindings.
// GL_PATCH_VERTICES; ES 3.2 spells it the same way.
void (*PatchParameteri)(GLenum pname, GLint value);
void (*BeginTransformFeedback)(GLenum primitiveMode);
void (*EndTransformFeedback)();
// ARB_transform_feedback2. A backend that leaves these null keeps the single
// implicit capture span the frontend has always modelled; the frontend state
// (paused flag, per-object bindings) is tracked either way.
void (*PauseTransformFeedback)();
void (*ResumeTransformFeedback)();
void (*BindTransformFeedback)(GLuint name);
void (*DeleteTransformFeedback)(GLuint name);
Int64 (*GetGpuTimestampNs)(); // glGetInteger64v(GL_TIMESTAMP); 0 if unsupported
};
struct GlobalBackendFunctionsTable {
GLFunctionsTable GL;
void (*Present)();
// Optional: applies the app-requested eglSwapInterval to the native
// presentation path (null = backend keeps its own pacing policy).
void (*SetSwapInterval)(Int interval);
};
enum class WindowBackend {
Android,
X11,
@@ -258,58 +32,6 @@ namespace MobileGL {
Uint32 Height = 0;
};
// SHARED IMAGES (docs/archive/notes/anland/plan-ahb-dmabuf.md): server-allocated colour
// buffers exported to clients as dma-buf descriptors.
//
// The server-side view of one image, as a backend sees it: `NativeBuffer` is the image's
// AHardwareBuffer (null where there is none), `Fourcc` a DRM fourcc - ABGR8888 (R, G, B, A in
// memory, GL RGBA8) or XBGR8888 (alpha ignored); ARGB8888 / XRGB8888 images are stored the
// same way (SharedImageRegistry.h says why), so a backend binds every one as RGBA8.
struct SharedImageView {
Uint64 Id = 0;
Uint32 Width = 0;
Uint32 Height = 0;
Uint32 Fourcc = 0;
void* NativeBuffer = nullptr;
};
// The client-side answer of an allocation: the image's id and its exported descriptor
// (`Fd`, owned by the caller), with the plane layout a linux-dmabuf wl_buffer is created with.
struct SharedImageExport {
Uint64 Id = 0;
int Fd = -1;
Uint32 Width = 0;
Uint32 Height = 0;
Uint32 Fourcc = 0;
Uint32 Stride = 0;
Uint32 Offset = 0;
Uint64 Modifier = 0;
// A YUV image's Cb Cr plane: in the same descriptor, at this offset and pitch.
Uint32 Plane1Stride = 0;
Uint32 Plane1Offset = 0;
};
// An import of a two-plane (YUV) dma-buf: where its planes are in the one descriptor, and
// EGL_EXT_image_dma_buf_import's colour hints (0 = not given).
struct SharedImageImportLayout {
Uint32 PlaneCount = 1;
Uint32 Offset[2] = {0, 0};
Uint32 Pitch[2] = {0, 0};
Uint32 ColorSpace = 0;
Uint32 Range = 0;
Uint32 SitingX = 0;
Uint32 SitingY = 0;
// The DRM format modifier the import named; HasModifier false = none (the implicit one).
Bool HasModifier = false;
Uint64 Modifier = 0;
};
// THE DAMAGE OF THE PRESENT BEING APPLIED on this thread: the server's apply thread sets it
// around the backend's Present (from the present record), and a backend that can hand damage
// to its native present reads it there. Full whenever nothing set it.
const MG_Util::Damage::Region& CurrentPresentDamage();
void SetCurrentPresentDamage(const MG_Util::Damage::Region& damage);
class BackendObject {
public:
virtual ~BackendObject() = default;
@@ -582,6 +304,5 @@ namespace MobileGL {
};
extern UniquePtr<BackendObject>& pActiveBackendObject;
extern GlobalBackendFunctionsTable gBackendFunctionsTable;
} // namespace MG_Backend
} // namespace MobileGL
@@ -8,7 +8,7 @@
#pragma once
#include <Includes.h>
#include <MG_Backend/BackendObject.h>
#include <MG_Backend/BackendFunctions.h>
#include "MagmaSession.h"
#include "Renderer/VulkanRenderer.h"
+156
View File
@@ -0,0 +1,156 @@
// MobileGL - MobileGL/MG_Backend/FormatCapabilities.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
#include <BackendCaps.h>
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
namespace MobileGL::MG_Backend {
namespace {
const char* GetFormatCapabilitySupportString(const FormatCapabilityCache& cache, SizeT targetIndex,
SizeT formatIndex, FormatCapability capability) {
if (HasFormatCapability(cache.FullCaps[targetIndex][formatIndex], capability)) return "Full";
if (HasFormatCapability(cache.CaveatCaps[targetIndex][formatIndex], capability)) return "Caveat";
return "None";
}
SizeT GetPrintedFormatNameWidth() {
SizeT width = 0;
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const auto format = static_cast<TextureInternalFormat>(formatIndex);
width = std::max(width, MG_Util::ConvertTextureInternalFormatToString(format).size());
}
return width;
}
SizeT GetCapabilityColumnWidth(FormatCapability capability) {
SizeT width = std::strlen(GetFormatCapabilityName(capability));
width = std::max<SizeT>(width, std::strlen("Caveat"));
return width;
}
String BuildFormatCapabilityHeader(SizeT formatNameWidth) {
std::ostringstream line;
line << std::left << std::setw(static_cast<Int>(formatNameWidth)) << "";
for (FormatCapability capability : kReportedFormatCapabilities) {
line << " | " << std::left << std::setw(static_cast<Int>(GetCapabilityColumnWidth(capability)))
<< GetFormatCapabilityName(capability);
}
return line.str();
}
String BuildFormatCapabilityRow(const FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
SizeT formatNameWidth) {
const auto format = static_cast<TextureInternalFormat>(formatIndex);
std::ostringstream line;
line << std::left << std::setw(static_cast<Int>(formatNameWidth))
<< MG_Util::ConvertTextureInternalFormatToString(format);
for (FormatCapability capability : kReportedFormatCapabilities) {
line << " | " << std::left << std::setw(static_cast<Int>(GetCapabilityColumnWidth(capability)))
<< GetFormatCapabilitySupportString(cache, targetIndex, formatIndex, capability);
}
return line.str();
}
} // namespace
void FormatCapabilityCache::Clear() {
for (auto& row : FullCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : CaveatCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : SampleCounts) {
for (auto& counts : row) {
counts.clear();
}
}
}
Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability) {
return static_cast<Bool>(caps & capability);
}
SizeT GetFormatCapabilityTargetIndex(TextureTarget target) {
if (target == TextureTarget::Unknown || static_cast<Int>(target) < 0 ||
static_cast<SizeT>(target) >= kFormatCapabilityTextureTargetCount) {
return kFormatCapabilityTargetCount;
}
// An external texture is a 2D one wherever a format can go.
if (target == TextureTarget::External) return static_cast<SizeT>(TextureTarget::Texture2D);
return static_cast<SizeT>(target);
}
SizeT GetRenderbufferFormatCapabilityTargetIndex() {
return kFormatCapabilityRenderbufferTargetIndex;
}
const char* GetFormatCapabilityName(FormatCapability capability) {
switch (capability) {
case FormatCapability::Creatable:
return "Creatable";
case FormatCapability::Sampled:
return "Sampled";
case FormatCapability::LinearFilter:
return "LinearFilter";
case FormatCapability::GenerateMipmap:
return "GenerateMipmap";
case FormatCapability::TextureGather:
return "TextureGather";
case FormatCapability::TextureShadow:
return "TextureShadow";
case FormatCapability::FramebufferRenderable:
return "FramebufferRenderable";
case FormatCapability::FramebufferLayered:
return "FramebufferLayered";
case FormatCapability::MultisampleTexture:
return "MultisampleTexture";
case FormatCapability::MultisampleRenderbuffer:
return "MultisampleRenderbuffer";
case FormatCapability::ColorAttachment:
return "ColorAttachment";
case FormatCapability::DepthAttachment:
return "DepthAttachment";
case FormatCapability::StencilAttachment:
return "StencilAttachment";
case FormatCapability::TextureBuffer:
return "TextureBuffer";
}
return "Unknown";
}
String GetFormatCapabilityTargetName(SizeT targetIndex) {
if (targetIndex == kFormatCapabilityRenderbufferTargetIndex) {
return "Renderbuffer";
}
if (targetIndex >= kFormatCapabilityTextureTargetCount) {
return "Unknown";
}
return MG_Util::ConvertTextureTargetToString(static_cast<TextureTarget>(targetIndex));
}
void PrintFormatCapabilities(const FormatCapabilityCache& cache) {
const SizeT formatNameWidth = GetPrintedFormatNameWidth();
MGLOG_D("Backend format capabilities:");
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTargetCount; ++targetIndex) {
MGLOG_D("");
const String targetName = GetFormatCapabilityTargetName(targetIndex);
MGLOG_D("- %s", targetName.c_str());
const String header = BuildFormatCapabilityHeader(formatNameWidth);
MGLOG_D("%s", header.c_str());
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const String row = BuildFormatCapabilityRow(cache, targetIndex, formatIndex, formatNameWidth);
MGLOG_D("%s", row.c_str());
}
}
}
} // namespace MobileGL::MG_Backend
+58
View File
@@ -0,0 +1,58 @@
// MobileGL - MobileGL/MG_Backend/SharedImageTypes.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#pragma once
#include <Includes.h>
namespace MobileGL::MG_Backend {
// SHARED IMAGES (docs/archive/notes/anland/plan-ahb-dmabuf.md): server-allocated colour
// buffers exported to clients as dma-buf descriptors.
//
// The server-side view of one image, as a backend sees it: `NativeBuffer` is the image's
// AHardwareBuffer (null where there is none), `Fourcc` a DRM fourcc - ABGR8888 (R, G, B, A in
// memory, GL RGBA8) or XBGR8888 (alpha ignored); ARGB8888 / XRGB8888 images are stored the
// same way (SharedImageRegistry.h says why), so a backend binds every one as RGBA8.
struct SharedImageView {
Uint64 Id = 0;
Uint32 Width = 0;
Uint32 Height = 0;
Uint32 Fourcc = 0;
void* NativeBuffer = nullptr;
};
// The client-side answer of an allocation: the image's id and its exported descriptor
// (`Fd`, owned by the caller), with the plane layout a linux-dmabuf wl_buffer is created with.
struct SharedImageExport {
Uint64 Id = 0;
int Fd = -1;
Uint32 Width = 0;
Uint32 Height = 0;
Uint32 Fourcc = 0;
Uint32 Stride = 0;
Uint32 Offset = 0;
Uint64 Modifier = 0;
// A YUV image's Cb Cr plane: in the same descriptor, at this offset and pitch.
Uint32 Plane1Stride = 0;
Uint32 Plane1Offset = 0;
};
// An import of a two-plane (YUV) dma-buf: where its planes are in the one descriptor, and
// EGL_EXT_image_dma_buf_import's colour hints (0 = not given).
struct SharedImageImportLayout {
Uint32 PlaneCount = 1;
Uint32 Offset[2] = {0, 0};
Uint32 Pitch[2] = {0, 0};
Uint32 ColorSpace = 0;
Uint32 Range = 0;
Uint32 SitingX = 0;
Uint32 SitingY = 0;
// The DRM format modifier the import named; HasModifier false = none (the implicit one).
Bool HasModifier = false;
Uint64 Modifier = 0;
};
} // namespace MobileGL::MG_Backend
+1 -1
View File
@@ -7,7 +7,7 @@
// End of Source File Header
#include "GL_Sync.h"
#include <MG_Backend/BackendObjects.h>
#include <MG_Backend/BackendFunctions.h>
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/BufferState/BufferObject.h>
#include <MG_Impl/Pipe/PipeFill.h>
+1 -1
View File
@@ -15,7 +15,7 @@
#pragma once
#include <Includes.h>
#include <MG_Backend/BackendObject.h>
#include <MG_Backend/BackendFunctions.h>
#include <MG_Pipe/MGPipeTypes.h>
#include <MG_Pipe/MGPipeValueTypes.h>
#include <MG_Pipe/PipeVerbSink.h>
@@ -30,7 +30,7 @@
// the pixels the draw produced or the case fails.
//
// 3. THE FIRST CASE ENDS WITH EndFrame(), DELIBERATELY. `Present` has ZERO call sites in
// MG_Impl - it is reached only through EGLImpl.cpp:178 -> BackendObject.cpp:396 - so a
// MG_Impl - it is reached only through eglSwapBuffers -> BackendObject::SwapEGLBuffers - so a
// scenario that never swaps never touches it, and B would then exercise a STRICT SUBSET of
// what ClearThenReadPixelsScenario already covers. BRIEF-P5 4.B lists Present(67) among the
// catalogue rows this target needs, so the frame boundary is here on purpose: B is A's slot
@@ -8,7 +8,7 @@
// G5b: the verb enum, the verb classes and their may-read field masks.
//
// GENERATED by scripts/gen_pipe.py from FillPoints.def, Coverage.def and MG_Backend/BackendObject.h - DO NOT EDIT.
// GENERATED by scripts/gen_pipe.py from FillPoints.def, Coverage.def and MG_Backend/BackendFunctions.h - DO NOT EDIT.
// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result.
// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe.
@@ -436,7 +436,7 @@ namespace MobileGL::MG_Remote::Client {
Bool BackendObject_Remote::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) {
// NOT FORWARDED, and this is the one that must not be. The base implementation's last
// act is GetBackendFunctions().Present() (BackendObject.cpp:396) - which is this
// act is GetBackendFunctions().Present() - which is this
// client's class-B Present EMITTER, the only route by which Present is reached at all
// (it has zero MG_Impl call sites). Calling Server::ServerSwapEGLBuffers would present
// on the server directly and put no record on the wire, which is the shape every gate
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#include <Includes.h>
#include <MG_Backend/BackendObject.h>
#include <MG_Backend/BackendFunctions.h>
// P5b: the clear discriminants below alias MGPipeTypes.h's (CONTRACT-P5B.md f1). The header
// was already in this file's closure through BackendObject.h's neighbours; naming it makes
// the dependency the aliases have explicit.
+1 -1
View File
@@ -19,7 +19,7 @@
#include <Includes.h>
#include <Config.h>
#include <MG_Backend/BackendObject.h>
#include <MG_Backend/BackendFunctions.h>
#if MOBILEGL_BUILD_DISAGGREGATED
#include <MG_Pipe/MGPipeTypes.h>
+1 -1
View File
@@ -17,7 +17,7 @@
#include "Init.h"
#include <Config.h>
#include <MG_Backend/BackendObjects.h>
#include <MG_Backend/BackendFunctions.h>
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
#include <MG_Impl/GLImpl/Query/GL_Query.h>
#include <MG_State/GLState/Core.h>
@@ -15,7 +15,7 @@
#include "Includes.h"
#include "Init.h"
#include <MG_Backend/BackendObjects.h>
#include <MG_Backend/BackendFunctions.h>
#include <MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h>
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"allcaps_comment_lines": 2253,
"line_citations": 159,
"line_citations": 156,
"long_comment_excess_lines": 8895,
"non_english_comment_lines": 20,
"planning_code_comment_lines": 3188
+3 -3
View File
@@ -45,7 +45,7 @@ REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PIPE_DIR = os.path.join(REPO_ROOT, "MobileGL", "MG_Pipe")
GENERATED_DIR = os.path.join(PIPE_DIR, "generated")
INVENTORY = os.path.join(REPO_ROOT, "scripts", "data", "backend_read_inventory.md")
FUNCTION_TABLE_HEADER = os.path.join(REPO_ROOT, "MobileGL", "MG_Backend", "BackendObject.h")
FUNCTION_TABLE_HEADER = os.path.join(REPO_ROOT, "MobileGL", "MG_Backend", "BackendFunctions.h")
GENERATED_BANNER = """// MobileGL - MobileGL/MG_Pipe/generated/{name}
// Copyright (c) 2025-2026 MobileGL-Dev
@@ -227,7 +227,7 @@ MEMCMP_FALLBACK_TYPES = set()
# Where the structs named in PipeFields.def are declared: the payload header, the value
# header, the host-span header, the backend capability header (DynamicBackendParameters, the
# caps block) and the backend object header.
# caps block) and the backend function table header.
FIELD_LIST_STRUCT_HEADERS = [
os.path.join(PIPE_DIR, "MGPipeTypes.h"),
os.path.join(PIPE_DIR, "MGPipeValueTypes.h"),
@@ -1344,7 +1344,7 @@ def gen_fill_points(accessors, sticky, verbs, classes, fields):
words = max(2, (len(accessors) + 63) // 64)
sticky_names = [name for name, _ in sticky]
out = [banner("PipeFillPoints.inc", "G5b: the verb enum, the verb classes and their may-read field masks.",
"FillPoints.def, Coverage.def and MG_Backend/BackendObject.h")]
"FillPoints.def, Coverage.def and MG_Backend/BackendFunctions.h")]
out.append("""// One verb per function-pointer member of MG_Backend::GLFunctionsTable, in declaration
// order, so the enum IS the table's member list. MG_Impl spells MGP_FILL(Verb) before every
// call through the table; MGPipeFillForVerb fills exactly the fields of the verb's class