From 55fa9cdfdca0bf317924d286e1207c4e15ee9449 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 16 Oct 2026 23:30:20 -0400 Subject: [PATCH] [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 --- CMakeLists.txt | 2 + MobileGL/MG_Backend/BackendFunctions.cpp | 25 ++ MobileGL/MG_Backend/BackendFunctions.h | 244 +++++++++++++++ MobileGL/MG_Backend/BackendObject.cpp | 156 ---------- MobileGL/MG_Backend/BackendObject.h | 283 +----------------- .../MG_Backend/DirectVulkan/DirectVulkan.h | 2 +- MobileGL/MG_Backend/FormatCapabilities.cpp | 156 ++++++++++ MobileGL/MG_Backend/SharedImageTypes.h | 58 ++++ MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp | 2 +- MobileGL/MG_Impl/Pipe/Verb/VerbPort.h | 2 +- .../Scenarios/TriangleScenario.cpp | 2 +- MobileGL/MG_Pipe/generated/PipeFillPoints.inc | 2 +- .../MG_Remote/Client/BackendObject_Remote.cpp | 2 +- MobileGL/MG_Remote/Client/EmitTables.h | 2 +- MobileGL/MG_Remote/Client/SlotCaps.h | 2 +- MobileGL/MG_Test/Query/QueryTest.cpp | 2 +- .../MG_Test/Texture/TextureByNameTest.cpp | 2 +- scripts/data/text_hygiene_baseline.json | 2 +- scripts/gen_pipe.py | 6 +- 19 files changed, 501 insertions(+), 451 deletions(-) create mode 100644 MobileGL/MG_Backend/BackendFunctions.cpp create mode 100644 MobileGL/MG_Backend/BackendFunctions.h create mode 100644 MobileGL/MG_Backend/FormatCapabilities.cpp create mode 100644 MobileGL/MG_Backend/SharedImageTypes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0993f930..8f1bc238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/MobileGL/MG_Backend/BackendFunctions.cpp b/MobileGL/MG_Backend/BackendFunctions.cpp new file mode 100644 index 00000000..b38542f1 --- /dev/null +++ b/MobileGL/MG_Backend/BackendFunctions.cpp @@ -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 diff --git a/MobileGL/MG_Backend/BackendFunctions.h b/MobileGL/MG_Backend/BackendFunctions.h new file mode 100644 index 00000000..4da95b4b --- /dev/null +++ b/MobileGL/MG_Backend/BackendFunctions.h @@ -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 +#include "MG_State/GLState/TextureState/TextureEnum.h" +#include +#include + +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 Texture; + SharedPtr 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& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value); + void (*ClearNamedFramebufferfi)(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + void (*ClearNamedFramebufferiv)(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLint* value); + void (*ClearNamedFramebufferuiv)(const SharedPtr& 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& readFramebuffer, + const SharedPtr& 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& 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& 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& 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 diff --git a/MobileGL/MG_Backend/BackendObject.cpp b/MobileGL/MG_Backend/BackendObject.cpp index 8246536c..6888cbff 100644 --- a/MobileGL/MG_Backend/BackendObject.cpp +++ b/MobileGL/MG_Backend/BackendObject.cpp @@ -7,11 +7,7 @@ // End of Source File Header #include "BackendObject.h" -#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h" #include -#include -#include -#include 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(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(width, std::strlen("Caveat")); - return width; - } - - String BuildFormatCapabilityHeader(SizeT formatNameWidth) { - std::ostringstream line; - line << std::left << std::setw(static_cast(formatNameWidth)) << ""; - for (FormatCapability capability : kReportedFormatCapabilities) { - line << " | " << std::left << std::setw(static_cast(GetCapabilityColumnWidth(capability))) - << GetFormatCapabilityName(capability); - } - return line.str(); - } - - String BuildFormatCapabilityRow(const FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex, - SizeT formatNameWidth) { - const auto format = static_cast(formatIndex); - std::ostringstream line; - line << std::left << std::setw(static_cast(formatNameWidth)) - << MG_Util::ConvertTextureInternalFormatToString(format); - for (FormatCapability capability : kReportedFormatCapabilities) { - line << " | " << std::left << std::setw(static_cast(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(caps & capability); - } - - SizeT GetFormatCapabilityTargetIndex(TextureTarget target) { - if (target == TextureTarget::Unknown || static_cast(target) < 0 || - static_cast(target) >= kFormatCapabilityTextureTargetCount) { - return kFormatCapabilityTargetCount; - } - // An external texture is a 2D one wherever a format can go. - if (target == TextureTarget::External) return static_cast(TextureTarget::Texture2D); - return static_cast(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(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 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 lock(m_eglStateMutex); if (!m_eglDisplayInitialized || m_eglDisplay != dpy) { diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index e12b1778..70977197 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -9,238 +9,12 @@ #pragma once #include #include -#include "MG_State/GLState/TextureState/TextureEnum.h" +#include +#include #include -#include 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 Texture; - SharedPtr 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& framebuffer, - GLenum buffer, GLint drawbuffer, const GLfloat* value); - void (*ClearNamedFramebufferfi)(const SharedPtr& framebuffer, - GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); - void (*ClearNamedFramebufferiv)(const SharedPtr& framebuffer, - GLenum buffer, GLint drawbuffer, const GLint* value); - void (*ClearNamedFramebufferuiv)(const SharedPtr& 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& readFramebuffer, - const SharedPtr& 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& 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& 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& 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& pActiveBackendObject; - extern GlobalBackendFunctionsTable gBackendFunctionsTable; } // namespace MG_Backend } // namespace MobileGL diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h index 5a29bf69..8d6d6135 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include "MagmaSession.h" #include "Renderer/VulkanRenderer.h" diff --git a/MobileGL/MG_Backend/FormatCapabilities.cpp b/MobileGL/MG_Backend/FormatCapabilities.cpp new file mode 100644 index 00000000..7a8e662c --- /dev/null +++ b/MobileGL/MG_Backend/FormatCapabilities.cpp @@ -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 +#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h" +#include +#include +#include +#include + +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(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(width, std::strlen("Caveat")); + return width; + } + + String BuildFormatCapabilityHeader(SizeT formatNameWidth) { + std::ostringstream line; + line << std::left << std::setw(static_cast(formatNameWidth)) << ""; + for (FormatCapability capability : kReportedFormatCapabilities) { + line << " | " << std::left << std::setw(static_cast(GetCapabilityColumnWidth(capability))) + << GetFormatCapabilityName(capability); + } + return line.str(); + } + + String BuildFormatCapabilityRow(const FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex, + SizeT formatNameWidth) { + const auto format = static_cast(formatIndex); + std::ostringstream line; + line << std::left << std::setw(static_cast(formatNameWidth)) + << MG_Util::ConvertTextureInternalFormatToString(format); + for (FormatCapability capability : kReportedFormatCapabilities) { + line << " | " << std::left << std::setw(static_cast(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(caps & capability); + } + + SizeT GetFormatCapabilityTargetIndex(TextureTarget target) { + if (target == TextureTarget::Unknown || static_cast(target) < 0 || + static_cast(target) >= kFormatCapabilityTextureTargetCount) { + return kFormatCapabilityTargetCount; + } + // An external texture is a 2D one wherever a format can go. + if (target == TextureTarget::External) return static_cast(TextureTarget::Texture2D); + return static_cast(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(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 diff --git a/MobileGL/MG_Backend/SharedImageTypes.h b/MobileGL/MG_Backend/SharedImageTypes.h new file mode 100644 index 00000000..7dcee460 --- /dev/null +++ b/MobileGL/MG_Backend/SharedImageTypes.h @@ -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 + +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 diff --git a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp index 0bde1516..bb1e213c 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp @@ -7,7 +7,7 @@ // End of Source File Header #include "GL_Sync.h" -#include +#include #include #include #include diff --git a/MobileGL/MG_Impl/Pipe/Verb/VerbPort.h b/MobileGL/MG_Impl/Pipe/Verb/VerbPort.h index 447f96cf..07ede310 100644 --- a/MobileGL/MG_Impl/Pipe/Verb/VerbPort.h +++ b/MobileGL/MG_Impl/Pipe/Verb/VerbPort.h @@ -15,7 +15,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/MobileGL/MG_IntegrationTest/Scenarios/TriangleScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/TriangleScenario.cpp index 52270ca6..32efe847 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/TriangleScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/TriangleScenario.cpp @@ -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 diff --git a/MobileGL/MG_Pipe/generated/PipeFillPoints.inc b/MobileGL/MG_Pipe/generated/PipeFillPoints.inc index 79e30246..6d6b827e 100644 --- a/MobileGL/MG_Pipe/generated/PipeFillPoints.inc +++ b/MobileGL/MG_Pipe/generated/PipeFillPoints.inc @@ -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. diff --git a/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp b/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp index ba78701f..35738991 100644 --- a/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp +++ b/MobileGL/MG_Remote/Client/BackendObject_Remote.cpp @@ -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 diff --git a/MobileGL/MG_Remote/Client/EmitTables.h b/MobileGL/MG_Remote/Client/EmitTables.h index 9439528f..777d0de8 100644 --- a/MobileGL/MG_Remote/Client/EmitTables.h +++ b/MobileGL/MG_Remote/Client/EmitTables.h @@ -18,7 +18,7 @@ #pragma once #include -#include +#include // 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. diff --git a/MobileGL/MG_Remote/Client/SlotCaps.h b/MobileGL/MG_Remote/Client/SlotCaps.h index fc018321..00b9d915 100644 --- a/MobileGL/MG_Remote/Client/SlotCaps.h +++ b/MobileGL/MG_Remote/Client/SlotCaps.h @@ -19,7 +19,7 @@ #include #include -#include +#include #if MOBILEGL_BUILD_DISAGGREGATED #include diff --git a/MobileGL/MG_Test/Query/QueryTest.cpp b/MobileGL/MG_Test/Query/QueryTest.cpp index 126a0785..6f26bea3 100644 --- a/MobileGL/MG_Test/Query/QueryTest.cpp +++ b/MobileGL/MG_Test/Query/QueryTest.cpp @@ -17,7 +17,7 @@ #include "Init.h" #include -#include +#include #include #include #include diff --git a/MobileGL/MG_Test/Texture/TextureByNameTest.cpp b/MobileGL/MG_Test/Texture/TextureByNameTest.cpp index 80639f31..eed3bb7c 100644 --- a/MobileGL/MG_Test/Texture/TextureByNameTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureByNameTest.cpp @@ -15,7 +15,7 @@ #include "Includes.h" #include "Init.h" -#include +#include #include #include #include diff --git a/scripts/data/text_hygiene_baseline.json b/scripts/data/text_hygiene_baseline.json index 3fd3810b..ef928d9b 100644 --- a/scripts/data/text_hygiene_baseline.json +++ b/scripts/data/text_hygiene_baseline.json @@ -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 diff --git a/scripts/gen_pipe.py b/scripts/gen_pipe.py index 6ab4ea61..02fcbc40 100644 --- a/scripts/gen_pipe.py +++ b/scripts/gen_pipe.py @@ -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