mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Refactor] (MG_Backend, MG_Backend/DirectGLES, MG_Backend/DirectVulkan, MG_Impl, MG_Impl/EGLImpl, MG_Impl/GLImpl, MG_Impl/GLXImpl, MG_Pipe, MG_Remote/Client, MG_Remote/Server, MG_Test/Wire): split the server-only and client-only hooks out of BackendObject
- ServerHooks and ClientHooks (BackendRoles.h) hold the virtuals that only the server, or only a client of a remote server, calls, with the defaults they had; BackendObject::AsServer and AsClient return them, or null for a backend without the role - DirectGLES and DirectVulkan implement ServerHooks, BackendObject_Remote implements ClientHooks, and the test doubles that run as a server's backend take the server role - every call site keeps its answer: where no backend and a backend without the role answered differently, the backend check stays and the role check gives the old default - ServerLoop::BackendHooks is the server's accessor, asked at each use - comments that placed the function table in BackendObject.h name BackendFunctions.h
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
#include <Includes.h>
|
||||
#include <BackendCaps.h>
|
||||
#include <MG_Backend/BackendFunctions.h>
|
||||
#include <MG_Backend/SharedImageTypes.h>
|
||||
#include <MG_Backend/BackendRoles.h>
|
||||
#include <MG_Util/Damage/Damage.h>
|
||||
|
||||
namespace MobileGL {
|
||||
@@ -36,152 +36,15 @@ namespace MobileGL {
|
||||
public:
|
||||
virtual ~BackendObject() = default;
|
||||
|
||||
// ---- shared images, SERVER side (a backend implements these) ---------------------
|
||||
//
|
||||
// Copies the default framebuffer of the surface current on this (the apply) thread into
|
||||
// `image`, rows TOP FIRST - GL's top row (y = height-1) lands in the image's first memory
|
||||
// row, which is what a wl_buffer's first row is - scaled to the image's extent if the two
|
||||
// differ. The image is ORDERED THROUGH ITS SYNC STATE (SharedImageRegistry.h): the copy
|
||||
// waits for the image's pending read fences before it writes, and its own completion
|
||||
// is published with SharedImages::PublishWrite as a sync_file - the call returns once
|
||||
// the copy is submitted, never waiting for it on the CPU (a backend with no fence export
|
||||
// waits and publishes -1). A reader binding the image waits for that fence on its GPU
|
||||
// when the write generation moved, and at its frame boundary publishes its frame's fence
|
||||
// with PublishRead / ReadTracker. The application's GL state is untouched. False = not
|
||||
// supported / failed (logged).
|
||||
//
|
||||
// `region` (GL window coordinates, origin bottom-left) is the part of the frame to copy;
|
||||
// Full copies all of it. The rest of the image is left as it is - the client sends a
|
||||
// region only for an image that already holds the frame outside it - and a backend that
|
||||
// cannot be sure the image's contents survived (a target it has never written) copies
|
||||
// all of it regardless.
|
||||
virtual Bool BlitDefaultFramebufferToSharedImage(const SharedImageView& image,
|
||||
const MG_Util::Damage::Region& region) {
|
||||
(void)image, (void)region;
|
||||
return false;
|
||||
}
|
||||
|
||||
// EGL_BUFFER_AGE_EXT of the draw surface of the context current on this (the apply)
|
||||
// thread: how many presents ago the buffer the next frame draws into held the frame then
|
||||
// presented; 0 = unknown (the application repaints everything). Asking says the client
|
||||
// relies on the answer, so from then on the backend keeps what it presents.
|
||||
//
|
||||
// `damageRegionFollows`: the client declares the region it draws (SetCurrentDamageRegion)
|
||||
// before drawing. A driver whose only buffer age is EGL_KHR_partial_update's keeps a
|
||||
// buffer's content only outside that region, so without one coming it is not asked.
|
||||
virtual Int32 QueryCurrentBufferAge(Bool damageRegionFollows) {
|
||||
(void)damageRegionFollows;
|
||||
return 0;
|
||||
}
|
||||
// eglSetDamageRegionKHR for that draw surface: the frame draws `region` (GL window
|
||||
// coordinates; Full = all of it) and nothing outside it. A backend without the notion
|
||||
// ignores it.
|
||||
virtual Bool SetCurrentDamageRegion(const MG_Util::Damage::Region& region) {
|
||||
(void)region;
|
||||
return true;
|
||||
}
|
||||
// IMPLICIT SYNC BY FLUSH (docs/archive/notes/anland/plan-x11-gpu.md): the session
|
||||
// on this (the apply) thread ends a frame at a glFlush. Every shared image it used since
|
||||
// its last boundary is published as written and read by one fence of everything it
|
||||
// submitted (SharedImages::ReadTracker::PublishFrameAsWrite), and from now on its first
|
||||
// use of an image in a frame waits for that image's pending reads (it may be about to
|
||||
// overwrite what another session still samples). A backend without shared images has
|
||||
// nothing to publish.
|
||||
virtual Bool PublishSharedImageAccesses() { return true; }
|
||||
// EGL_ANDROID_native_fence_sync's fence command, on the server: in `*fence`, a sync_file
|
||||
// (the caller's to close) that signals once everything the session submitted so far has
|
||||
// completed - and, for a session that writes images, what PublishSharedImageAccesses
|
||||
// does. -1 when there is none to give: the backend cannot export one, or the work has
|
||||
// already completed - in both cases it HAS completed when this returns.
|
||||
virtual Bool ExportNativeFence(int* fence) {
|
||||
*fence = -1;
|
||||
return PublishSharedImageAccesses();
|
||||
}
|
||||
|
||||
// ---- shared images, CLIENT side (BackendObject_Remote implements these) ----------
|
||||
//
|
||||
// A new image, allocated by the server; `out->Fd` is the caller's to close.
|
||||
virtual Bool AllocateSharedImage(Uint32 width, Uint32 height, Uint32 fourcc, SharedImageExport* out) {
|
||||
(void)width, (void)height, (void)fourcc, (void)out;
|
||||
return false;
|
||||
}
|
||||
// Names the image `fd` was exported from (the caller keeps `fd`). False: not one of this
|
||||
// server's images, or the size/format disagree.
|
||||
virtual Bool ImportSharedImage(int fd, Uint32 width, Uint32 height, Uint32 fourcc, Uint64* outId) {
|
||||
(void)fd, (void)width, (void)height, (void)fourcc, (void)outId;
|
||||
return false;
|
||||
}
|
||||
// The same for a YUV fourcc, whose planes and hints `layout` gives. A YUV buffer the
|
||||
// server did not export is taken too (its labelled CPU-copy fallback).
|
||||
virtual Bool ImportSharedImagePlanes(int fd, Uint32 width, Uint32 height, Uint32 fourcc,
|
||||
const SharedImageImportLayout& layout, Uint64* outId) {
|
||||
(void)fd, (void)width, (void)height, (void)fourcc, (void)layout, (void)outId;
|
||||
return false;
|
||||
}
|
||||
// Drops this client's reference (an allocation's or an import's).
|
||||
virtual Bool ReleaseSharedImage(Uint64 id) {
|
||||
(void)id;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: the current context's default framebuffer, copied top row first into
|
||||
// the image (BlitDefaultFramebufferToSharedImage on the server) - `region` of it, Full for
|
||||
// all. Returns when it completed.
|
||||
virtual Bool PresentToSharedImage(Uint64 id, const MG_Util::Damage::Region& region) {
|
||||
(void)id, (void)region;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: EGL_BUFFER_AGE_EXT of the current draw surface (QueryCurrentBufferAge
|
||||
// on the server). False: the backend has no answer (the caller reports 0).
|
||||
virtual Bool QueryBufferAge(Bool damageRegionFollows, EGLint* age) {
|
||||
(void)damageRegionFollows, (void)age;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: eglSetDamageRegionKHR for the current draw surface
|
||||
// (SetCurrentDamageRegion on the server).
|
||||
virtual Bool SetDamageRegion(const MG_Util::Damage::Region& region) {
|
||||
(void)region;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: the texture whose lifetime id is `textureLifetimeId` takes the image as
|
||||
// its level-0 storage (glEGLImageTargetTexture2DOES), until its next storage definition.
|
||||
// The caller has already defined level 0 as Width x Height RGBA8 with no data.
|
||||
virtual Bool AttachSharedImageToTexture(Uint64 textureLifetimeId, Uint64 id) {
|
||||
(void)textureLifetimeId, (void)id;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream, from glFlush/glFinish: PublishSharedImageAccesses on the server,
|
||||
// returning once the fence is published. Skipped (true) when nothing reached the stream
|
||||
// since the last one.
|
||||
virtual Bool FlushSharedImageAccesses() { return false; }
|
||||
// In the GL stream, from eglCreateSync(EGL_SYNC_NATIVE_FENCE_ANDROID) with no descriptor:
|
||||
// ExportNativeFence on the server. `*fence` is the sync_file it handed back (the
|
||||
// caller's to close), or -1 when it had none - the fenced work has then completed.
|
||||
virtual Bool CreateNativeFence(int* fence) {
|
||||
*fence = -1;
|
||||
return false;
|
||||
}
|
||||
// The hooks only a server, or only a client of a remote server, calls (BackendRoles.h);
|
||||
// null for a backend that plays no such role.
|
||||
virtual ServerHooks* AsServer() { return nullptr; }
|
||||
virtual ClientHooks* AsClient() { return nullptr; }
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
virtual Bool InitCapabilities() = 0;
|
||||
virtual Bool InitWindowSurface() = 0;
|
||||
|
||||
// A served session's apply thread - the one thread that runs this backend from here on -
|
||||
// calls this first, in a process that may serve several sessions at once. A backend whose
|
||||
// native state is process-global by construction gives this object its own copy and binds
|
||||
// it to the calling thread. The default has nothing per process to separate.
|
||||
virtual void BindSessionStateToThisThread() {}
|
||||
// A served session's apply thread asks this each time it wakes, BEFORE it applies another
|
||||
// record: true when this session's own GPU work faulted or hung the GPU and the backend has
|
||||
// latched the session for it (MGPipeSessionLatch, family DeviceLost), so the next frame -
|
||||
// which would very likely stop the GPU again - is never applied. Cheap by contract: no
|
||||
// wait, at most a status read. The default knows of no fault.
|
||||
virtual Bool LatchIfGpuFaulted() { return false; }
|
||||
// A served session's ring moved to another of its client's contexts (bind_context). A
|
||||
// backend whose surface follows the native context it binds needs nothing; one that keeps
|
||||
// a single native surface binding re-binds the surface that context was last made current
|
||||
// with. `token` is the client's context token (the EGLContext MakeEGLCurrent was given).
|
||||
virtual void OnClientContextBound(Uint64 token) { (void)token; }
|
||||
|
||||
virtual Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor);
|
||||
virtual Bool CreateEGLWindowSurface(EGLSurface surface, const WindowHandle& handle);
|
||||
virtual Bool ResizeEGLWindowSurface(EGLSurface surface, Uint32 width, Uint32 height);
|
||||
@@ -201,36 +64,6 @@ namespace MobileGL {
|
||||
virtual void ReleaseEGLSurface(EGLSurface surface);
|
||||
virtual void ReleaseEGLResources();
|
||||
|
||||
// ---- the server's own display window going away and coming back -------------------
|
||||
//
|
||||
// `window` (a window surface's native window) is about to be destroyed - the display's
|
||||
// screen went off. Release ONLY what is bound to it: every window surface on it keeps its
|
||||
// client handle and draws into a placeholder from here on, and every context, object and
|
||||
// device stays. Called on the thread that runs this backend; nothing may reference the
|
||||
// window once it returns true. False: not supported (the caller ends the session instead).
|
||||
virtual Bool SuspendServerWindow(void* window) {
|
||||
(void)window;
|
||||
return false;
|
||||
}
|
||||
// The surfaces SuspendServerWindow parked are rebuilt on `window` (a new native window,
|
||||
// `width`x`height`), and their new extent is published as a creation publishes it. False:
|
||||
// they could not be (the caller ends the session).
|
||||
virtual Bool ResumeServerWindow(void* window, Uint32 width, Uint32 height) {
|
||||
(void)window, (void)width, (void)height;
|
||||
return false;
|
||||
}
|
||||
// `window` changed size to `width`x`height` (the display's report - the one size to publish).
|
||||
// Its surfaces publish that extent to the client now, without waiting for a present; a
|
||||
// swapchain that has to follow is rebuilt when no frame is half-recorded, else at the
|
||||
// present. False: nothing was published (no surface on it, or not supported).
|
||||
virtual Bool ServerWindowResized(void* window, Uint32 width, Uint32 height) {
|
||||
(void)window, (void)width, (void)height;
|
||||
return false;
|
||||
}
|
||||
// CLIENT side, from eglQuerySurface(EGL_WIDTH/EGL_HEIGHT): a backend whose surface extents
|
||||
// change under the application (a remote server's window) applies what it has received.
|
||||
virtual void RefreshSurfaceExtent(EGLSurface surface) { (void)surface; }
|
||||
|
||||
void SetWindowHandle(const WindowHandle& handle);
|
||||
|
||||
virtual const RendererInfo& GetRendererInfo() const = 0;
|
||||
@@ -303,6 +136,14 @@ namespace MobileGL {
|
||||
void ReleaseEGLCurrentThread(const std::thread::id& threadKey);
|
||||
};
|
||||
|
||||
// A backend's role hooks; null for no backend, or one that plays no such role.
|
||||
inline ServerHooks* ServerHooksOf(BackendObject* backend) {
|
||||
return backend != nullptr ? backend->AsServer() : nullptr;
|
||||
}
|
||||
inline ClientHooks* ClientHooksOf(BackendObject* backend) {
|
||||
return backend != nullptr ? backend->AsClient() : nullptr;
|
||||
}
|
||||
|
||||
extern UniquePtr<BackendObject>& pActiveBackendObject;
|
||||
} // namespace MG_Backend
|
||||
} // namespace MobileGL
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// MobileGL - MobileGL/MG_Backend/BackendRoles.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_Backend/SharedImageTypes.h>
|
||||
#include <MG_Util/Damage/Damage.h>
|
||||
|
||||
namespace MobileGL::MG_Backend {
|
||||
// What only the server asks of the backend it applies a session's records with. A backend that
|
||||
// can serve sessions implements it and returns it from BackendObject::AsServer; each default is
|
||||
// the answer of a backend with nothing to do for that hook.
|
||||
class ServerHooks {
|
||||
public:
|
||||
// ---- shared images ------------------------------------------------------------------
|
||||
//
|
||||
// Copies the default framebuffer of the surface current on this (the apply) thread into
|
||||
// `image`, rows TOP FIRST - GL's top row (y = height-1) lands in the image's first memory
|
||||
// row, which is what a wl_buffer's first row is - scaled to the image's extent if the two
|
||||
// differ. The image is ORDERED THROUGH ITS SYNC STATE (SharedImageRegistry.h): the copy
|
||||
// waits for the image's pending read fences before it writes, and its own completion
|
||||
// is published with SharedImages::PublishWrite as a sync_file - the call returns once
|
||||
// the copy is submitted, never waiting for it on the CPU (a backend with no fence export
|
||||
// waits and publishes -1). A reader binding the image waits for that fence on its GPU
|
||||
// when the write generation moved, and at its frame boundary publishes its frame's fence
|
||||
// with PublishRead / ReadTracker. The application's GL state is untouched. False = not
|
||||
// supported / failed (logged).
|
||||
//
|
||||
// `region` (GL window coordinates, origin bottom-left) is the part of the frame to copy;
|
||||
// Full copies all of it. The rest of the image is left as it is - the client sends a
|
||||
// region only for an image that already holds the frame outside it - and a backend that
|
||||
// cannot be sure the image's contents survived (a target it has never written) copies
|
||||
// all of it regardless.
|
||||
virtual Bool BlitDefaultFramebufferToSharedImage(const SharedImageView& image,
|
||||
const MG_Util::Damage::Region& region) {
|
||||
(void)image, (void)region;
|
||||
return false;
|
||||
}
|
||||
|
||||
// EGL_BUFFER_AGE_EXT of the draw surface of the context current on this (the apply)
|
||||
// thread: how many presents ago the buffer the next frame draws into held the frame then
|
||||
// presented; 0 = unknown (the application repaints everything). Asking says the client
|
||||
// relies on the answer, so from then on the backend keeps what it presents.
|
||||
//
|
||||
// `damageRegionFollows`: the client declares the region it draws (SetCurrentDamageRegion)
|
||||
// before drawing. A driver whose only buffer age is EGL_KHR_partial_update's keeps a
|
||||
// buffer's content only outside that region, so without one coming it is not asked.
|
||||
virtual Int32 QueryCurrentBufferAge(Bool damageRegionFollows) {
|
||||
(void)damageRegionFollows;
|
||||
return 0;
|
||||
}
|
||||
// eglSetDamageRegionKHR for that draw surface: the frame draws `region` (GL window
|
||||
// coordinates; Full = all of it) and nothing outside it. A backend without the notion
|
||||
// ignores it.
|
||||
virtual Bool SetCurrentDamageRegion(const MG_Util::Damage::Region& region) {
|
||||
(void)region;
|
||||
return true;
|
||||
}
|
||||
// IMPLICIT SYNC BY FLUSH (docs/archive/notes/anland/plan-x11-gpu.md): the session
|
||||
// on this (the apply) thread ends a frame at a glFlush. Every shared image it used since
|
||||
// its last boundary is published as written and read by one fence of everything it
|
||||
// submitted (SharedImages::ReadTracker::PublishFrameAsWrite), and from now on its first
|
||||
// use of an image in a frame waits for that image's pending reads (it may be about to
|
||||
// overwrite what another session still samples). A backend without shared images has
|
||||
// nothing to publish.
|
||||
virtual Bool PublishSharedImageAccesses() { return true; }
|
||||
// EGL_ANDROID_native_fence_sync's fence command, on the server: in `*fence`, a sync_file
|
||||
// (the caller's to close) that signals once everything the session submitted so far has
|
||||
// completed - and, for a session that writes images, what PublishSharedImageAccesses
|
||||
// does. -1 when there is none to give: the backend cannot export one, or the work has
|
||||
// already completed - in both cases it HAS completed when this returns.
|
||||
virtual Bool ExportNativeFence(int* fence) {
|
||||
*fence = -1;
|
||||
return PublishSharedImageAccesses();
|
||||
}
|
||||
|
||||
// A served session's apply thread - the one thread that runs this backend from here on -
|
||||
// calls this first, in a process that may serve several sessions at once. A backend whose
|
||||
// native state is process-global by construction gives this object its own copy and binds
|
||||
// it to the calling thread. The default has nothing per process to separate.
|
||||
virtual void BindSessionStateToThisThread() {}
|
||||
// A served session's apply thread asks this each time it wakes, BEFORE it applies another
|
||||
// record: true when this session's own GPU work faulted or hung the GPU and the backend has
|
||||
// latched the session for it (MGPipeSessionLatch, family DeviceLost), so the next frame -
|
||||
// which would very likely stop the GPU again - is never applied. Cheap by contract: no
|
||||
// wait, at most a status read. The default knows of no fault.
|
||||
virtual Bool LatchIfGpuFaulted() { return false; }
|
||||
// A served session's ring moved to another of its client's contexts (bind_context). A
|
||||
// backend whose surface follows the native context it binds needs nothing; one that keeps
|
||||
// a single native surface binding re-binds the surface that context was last made current
|
||||
// with. `token` is the client's context token (the EGLContext MakeEGLCurrent was given).
|
||||
virtual void OnClientContextBound(Uint64 token) { (void)token; }
|
||||
|
||||
// ---- the server's own display window going away and coming back -------------------
|
||||
//
|
||||
// `window` (a window surface's native window) is about to be destroyed - the display's
|
||||
// screen went off. Release ONLY what is bound to it: every window surface on it keeps its
|
||||
// client handle and draws into a placeholder from here on, and every context, object and
|
||||
// device stays. Called on the thread that runs this backend; nothing may reference the
|
||||
// window once it returns true. False: not supported (the caller ends the session instead).
|
||||
virtual Bool SuspendServerWindow(void* window) {
|
||||
(void)window;
|
||||
return false;
|
||||
}
|
||||
// The surfaces SuspendServerWindow parked are rebuilt on `window` (a new native window,
|
||||
// `width`x`height`), and their new extent is published as a creation publishes it. False:
|
||||
// they could not be (the caller ends the session).
|
||||
virtual Bool ResumeServerWindow(void* window, Uint32 width, Uint32 height) {
|
||||
(void)window, (void)width, (void)height;
|
||||
return false;
|
||||
}
|
||||
// `window` changed size to `width`x`height` (the display's report - the one size to publish).
|
||||
// Its surfaces publish that extent to the client now, without waiting for a present; a
|
||||
// swapchain that has to follow is rebuilt when no frame is half-recorded, else at the
|
||||
// present. False: nothing was published (no surface on it, or not supported).
|
||||
virtual Bool ServerWindowResized(void* window, Uint32 width, Uint32 height) {
|
||||
(void)window, (void)width, (void)height;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
~ServerHooks() = default;
|
||||
};
|
||||
|
||||
// What only a client of a remote server asks of its backend. BackendObject_Remote implements it
|
||||
// and returns it from BackendObject::AsClient; each default is the answer of a backend with no
|
||||
// server behind it.
|
||||
class ClientHooks {
|
||||
public:
|
||||
// ---- shared images ------------------------------------------------------------------
|
||||
//
|
||||
// A new image, allocated by the server; `out->Fd` is the caller's to close.
|
||||
virtual Bool AllocateSharedImage(Uint32 width, Uint32 height, Uint32 fourcc, SharedImageExport* out) {
|
||||
(void)width, (void)height, (void)fourcc, (void)out;
|
||||
return false;
|
||||
}
|
||||
// Names the image `fd` was exported from (the caller keeps `fd`). False: not one of this
|
||||
// server's images, or the size/format disagree.
|
||||
virtual Bool ImportSharedImage(int fd, Uint32 width, Uint32 height, Uint32 fourcc, Uint64* outId) {
|
||||
(void)fd, (void)width, (void)height, (void)fourcc, (void)outId;
|
||||
return false;
|
||||
}
|
||||
// The same for a YUV fourcc, whose planes and hints `layout` gives. A YUV buffer the
|
||||
// server did not export is taken too (its labelled CPU-copy fallback).
|
||||
virtual Bool ImportSharedImagePlanes(int fd, Uint32 width, Uint32 height, Uint32 fourcc,
|
||||
const SharedImageImportLayout& layout, Uint64* outId) {
|
||||
(void)fd, (void)width, (void)height, (void)fourcc, (void)layout, (void)outId;
|
||||
return false;
|
||||
}
|
||||
// Drops this client's reference (an allocation's or an import's).
|
||||
virtual Bool ReleaseSharedImage(Uint64 id) {
|
||||
(void)id;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: the current context's default framebuffer, copied top row first into
|
||||
// the image (BlitDefaultFramebufferToSharedImage on the server) - `region` of it, Full for
|
||||
// all. Returns when it completed.
|
||||
virtual Bool PresentToSharedImage(Uint64 id, const MG_Util::Damage::Region& region) {
|
||||
(void)id, (void)region;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: EGL_BUFFER_AGE_EXT of the current draw surface (QueryCurrentBufferAge
|
||||
// on the server). False: the backend has no answer (the caller reports 0).
|
||||
virtual Bool QueryBufferAge(Bool damageRegionFollows, EGLint* age) {
|
||||
(void)damageRegionFollows, (void)age;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: eglSetDamageRegionKHR for the current draw surface
|
||||
// (SetCurrentDamageRegion on the server).
|
||||
virtual Bool SetDamageRegion(const MG_Util::Damage::Region& region) {
|
||||
(void)region;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream: the texture whose lifetime id is `textureLifetimeId` takes the image as
|
||||
// its level-0 storage (glEGLImageTargetTexture2DOES), until its next storage definition.
|
||||
// The caller has already defined level 0 as Width x Height RGBA8 with no data.
|
||||
virtual Bool AttachSharedImageToTexture(Uint64 textureLifetimeId, Uint64 id) {
|
||||
(void)textureLifetimeId, (void)id;
|
||||
return false;
|
||||
}
|
||||
// In the GL stream, from glFlush/glFinish: PublishSharedImageAccesses on the server,
|
||||
// returning once the fence is published. Skipped (true) when nothing reached the stream
|
||||
// since the last one.
|
||||
virtual Bool FlushSharedImageAccesses() { return false; }
|
||||
// In the GL stream, from eglCreateSync(EGL_SYNC_NATIVE_FENCE_ANDROID) with no descriptor:
|
||||
// ExportNativeFence on the server. `*fence` is the sync_file it handed back (the
|
||||
// caller's to close), or -1 when it had none - the fenced work has then completed.
|
||||
virtual Bool CreateNativeFence(int* fence) {
|
||||
*fence = -1;
|
||||
return false;
|
||||
}
|
||||
// From eglQuerySurface(EGL_WIDTH/EGL_HEIGHT): a backend whose surface extents
|
||||
// change under the application (a remote server's window) applies what it has received.
|
||||
virtual void RefreshSurfaceExtent(EGLSurface surface) { (void)surface; }
|
||||
|
||||
protected:
|
||||
~ClientHooks() = default;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend
|
||||
@@ -27,10 +27,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Int ClampSamplesToBackendSupport(SizeT targetIndex, TextureInternalFormat logicalFormat, GLenum imageFormat,
|
||||
Int samples);
|
||||
|
||||
class BackendObject_DirectGLES : public BackendObject {
|
||||
class BackendObject_DirectGLES : public BackendObject, public ServerHooks {
|
||||
public:
|
||||
~BackendObject_DirectGLES() override;
|
||||
|
||||
ServerHooks* AsServer() override { return this; }
|
||||
|
||||
// The driver's verdict on this session's context (DirectGLES::LatchIfDeviceLost).
|
||||
Bool LatchIfGpuFaulted() override;
|
||||
void Initialize() override;
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// `debugKnob` false: the knob is not consulted here (the on-screen Present, so an injected loss
|
||||
// lands on an offscreen client's shared-image present or readback, never on the compositor's).
|
||||
Bool LatchIfDeviceLost(const char* site, Bool debugKnob = true);
|
||||
// The apply loop's form of it (BackendObject::LatchIfGpuFaulted), asked each time the apply thread
|
||||
// The apply loop's form of it (ServerHooks::LatchIfGpuFaulted), asked each time the apply thread
|
||||
// wakes: the driver's report only, no debug knob, and no EGL ground-truth verification spent.
|
||||
Bool LatchIfGuiltyBeforeApply();
|
||||
#endif
|
||||
@@ -536,7 +536,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image) on what the active unit has bound to
|
||||
// GL_TEXTURE_2D. False when the driver refused it (its GL error is consumed).
|
||||
Bool TargetBoundTexture2D(const EglImageRef& image);
|
||||
// BackendObject::BlitDefaultFramebufferToSharedImage, for the calling thread's context.
|
||||
// ServerHooks::BlitDefaultFramebufferToSharedImage, for the calling thread's context.
|
||||
Bool BlitDefaultFramebufferTo(const SharedImageView& image, const MG_Util::Damage::Region& region);
|
||||
|
||||
// THE READER'S HALF of the image's sync state (SharedImageRegistry.h). Called on every use
|
||||
@@ -556,12 +556,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// at Present and wherever the current native context stops being current (its reads can
|
||||
// only be fenced from it). Nothing noted = nothing done.
|
||||
void PublishPendingReads();
|
||||
// IMPLICIT SYNC BY FLUSH (BackendObject::PublishSharedImageAccesses): everything the
|
||||
// IMPLICIT SYNC BY FLUSH (ServerHooks::PublishSharedImageAccesses): everything the
|
||||
// calling session used since its last boundary is published as written and read by one
|
||||
// fence, and the session's first use of an image in each frame from now on also waits for
|
||||
// the image's pending reads.
|
||||
Bool PublishPendingAccesses();
|
||||
// EGL_ANDROID_native_fence_sync's fence command (BackendObject::ExportNativeFence): a
|
||||
// EGL_ANDROID_native_fence_sync's fence command (ServerHooks::ExportNativeFence): a
|
||||
// sync_file of everything the current context submitted in `*fence` (-1: none, and the work
|
||||
// is waited out here), and - for a session that writes images - the boundary
|
||||
// PublishPendingAccesses makes.
|
||||
|
||||
@@ -20,11 +20,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
PFN_vkGetPhysicalDeviceFormatProperties getFormatProperties,
|
||||
const MG_External::VulkanCapabilities& capabilities, FormatCapabilityCache& cache);
|
||||
|
||||
class BackendObject_DirectVulkan : public BackendObject {
|
||||
class BackendObject_DirectVulkan : public BackendObject, public ServerHooks {
|
||||
public:
|
||||
BackendObject_DirectVulkan();
|
||||
~BackendObject_DirectVulkan() override;
|
||||
|
||||
ServerHooks* AsServer() override { return this; }
|
||||
|
||||
void Initialize() override;
|
||||
void BindSessionStateToThisThread() override;
|
||||
// The GPU hang watch's verdict on this session's device (VulkanRenderer::LatchIfGpuHung).
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void* getAhbProperties = nullptr; // PFN_vkGetAndroidHardwareBufferPropertiesANDROID
|
||||
};
|
||||
Bool GetWireAhbImport(WireAhbImport& out) const;
|
||||
// SHARED IMAGES (BackendObject::BlitDefaultFramebufferToSharedImage): the active surface
|
||||
// SHARED IMAGES (ServerHooks::BlitDefaultFramebufferToSharedImage): the active surface
|
||||
// target's default framebuffer, copied into `image` top row first and scaled to its
|
||||
// extent. Returns once the copy is submitted, with its fence published to the image
|
||||
// (SharedImages::PublishWrite); waits for it on the CPU only on a device without sync_file
|
||||
@@ -225,12 +225,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// renderer has written before - the rest of it is left as it is; a first write, a scaled
|
||||
// or turned copy copies all of it.
|
||||
Bool BlitDefaultFramebufferToSharedImage(const SharedImageView& image, const MG_Util::Damage::Region& region);
|
||||
// IMPLICIT SYNC BY FLUSH (BackendObject::PublishSharedImageAccesses): every image acquired
|
||||
// IMPLICIT SYNC BY FLUSH (ServerHooks::PublishSharedImageAccesses): every image acquired
|
||||
// since the last boundary goes back to the foreign family, the recording is submitted with
|
||||
// the exportable semaphore, and its sync_file is published to each as a write and a read.
|
||||
// From then on an image's first acquire per frame also waits for its pending reads.
|
||||
Bool PublishSharedImageAccesses();
|
||||
// EGL_ANDROID_native_fence_sync's fence command (BackendObject::ExportNativeFence): the
|
||||
// EGL_ANDROID_native_fence_sync's fence command (ServerHooks::ExportNativeFence): the
|
||||
// submission's sync_file in `*fence` (-1: none, and the work is waited out here), and - for
|
||||
// a session that writes images - the boundary PublishSharedImageAccesses makes.
|
||||
Bool ExportNativeFence(int* fence);
|
||||
|
||||
@@ -1194,8 +1194,9 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
}
|
||||
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
|
||||
// The display's dma-buf EGLImages went with it; so do their shared-image references.
|
||||
MG_Backend::ClientHooks* client = backendObject->AsClient();
|
||||
for (const Uint64 sharedImageId : state->TakeOrphanedSharedImages()) {
|
||||
(void)backendObject->ReleaseSharedImage(sharedImageId);
|
||||
if (client != nullptr) (void)client->ReleaseSharedImage(sharedImageId);
|
||||
}
|
||||
backendObject->ReleaseEGLResources();
|
||||
}
|
||||
@@ -1305,9 +1306,9 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (StreamGateActive()) StreamBindCallingThreadLocked();
|
||||
#endif
|
||||
EGLint age = 0;
|
||||
auto* backendObject = MG_Backend::pActiveBackendObject.get();
|
||||
MG_Backend::ClientHooks* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
const Bool regionFollows = DamageRegionSurfaces().count(surface) != 0;
|
||||
if (backendObject == nullptr || !backendObject->QueryBufferAge(regionFollows, &age)) age = 0;
|
||||
if (client == nullptr || !client->QueryBufferAge(regionFollows, &age)) age = 0;
|
||||
*value = age;
|
||||
return EGL_TRUE;
|
||||
}
|
||||
@@ -1315,8 +1316,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// backend: let it bring the surface's record up to date first, so a caller that polls the size
|
||||
// while it draws nothing still sees the change.
|
||||
if (attribute == EGL_WIDTH || attribute == EGL_HEIGHT) {
|
||||
if (auto* backendObject = MG_Backend::pActiveBackendObject.get())
|
||||
backendObject->RefreshSurfaceExtent(surface);
|
||||
if (auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get()))
|
||||
client->RefreshSurfaceExtent(surface);
|
||||
}
|
||||
return state->QuerySurface(display, surface, attribute, value) ? EGL_TRUE : EGL_FALSE;
|
||||
}
|
||||
@@ -1358,7 +1359,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (StreamGateActive()) StreamBindCallingThreadLocked();
|
||||
#endif
|
||||
DamageRegionSurfaces().insert(surface);
|
||||
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) (void)backendObject->SetDamageRegion(region);
|
||||
if (auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get()))
|
||||
(void)client->SetDamageRegion(region);
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
@@ -1618,8 +1620,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
{
|
||||
const GLStreamScope stream;
|
||||
MG_Pipe::MGPipeClientDrainTextureUploads();
|
||||
auto* backendObject = MG_Backend::pActiveBackendObject.get();
|
||||
if (backendObject == nullptr || !backendObject->CreateNativeFence(&fence)) {
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
if (client == nullptr || !client->CreateNativeFence(&fence)) {
|
||||
MGLOG_E_ONCE("eglCreateSync: the server did not answer a native fence; none is created");
|
||||
state->SetError(EGL_BAD_ALLOC);
|
||||
return EGL_NO_SYNC;
|
||||
@@ -1690,13 +1692,14 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (known >= 0) return known != 0;
|
||||
auto* backendObject = MG_Backend::pActiveBackendObject.get();
|
||||
if (backendObject == nullptr) return false;
|
||||
MG_Backend::ClientHooks* client = backendObject->AsClient();
|
||||
MG_Backend::SharedImageExport probe{};
|
||||
const Bool allocated =
|
||||
backendObject->AllocateSharedImage(16, 16, MG_State::EGLState::EGLContext::kDrmFourccNv12, &probe);
|
||||
const Bool allocated = client != nullptr && client->AllocateSharedImage(
|
||||
16, 16, MG_State::EGLState::EGLContext::kDrmFourccNv12, &probe);
|
||||
#if !defined(_WIN32)
|
||||
if (probe.Fd >= 0) ::close(probe.Fd);
|
||||
#endif
|
||||
if (allocated && probe.Id != 0) (void)backendObject->ReleaseSharedImage(probe.Id);
|
||||
if (allocated && probe.Id != 0) (void)client->ReleaseSharedImage(probe.Id);
|
||||
answer.store(allocated ? 1 : 0, std::memory_order_release);
|
||||
MGLOG_I("Shared images: the server %s YUV (NV12) images", allocated ? "holds" : "has no");
|
||||
return allocated;
|
||||
@@ -1751,7 +1754,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// Uploads still waiting for a validate point go first: they are part of what is published.
|
||||
MG_Pipe::MGPipeClientDrainTextureUploads();
|
||||
#endif
|
||||
if (backendObject != nullptr && !backendObject->FlushSharedImageAccesses()) {
|
||||
MG_Backend::ClientHooks* client = MG_Backend::ClientHooksOf(backendObject);
|
||||
if (backendObject != nullptr && (client == nullptr || !client->FlushSharedImageAccesses())) {
|
||||
MGLOG_E_ONCE("glFlush: this session's shared-image accesses could not be published; another session may "
|
||||
"read them early");
|
||||
}
|
||||
@@ -1778,7 +1782,9 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (!state->PrepareDmaBufImport(dpy, ctx, buffer, attrib_list, &request)) {
|
||||
return EGL_NO_IMAGE;
|
||||
}
|
||||
auto* backendObject = GetBackendObject(state);
|
||||
// GetBackendObject reports the missing backend; a backend that is no remote server's client
|
||||
// imports nothing.
|
||||
MG_Backend::ClientHooks* client = MG_Backend::ClientHooksOf(GetBackendObject(state));
|
||||
Uint64 sharedImageId = 0;
|
||||
Bool imported = false;
|
||||
if (MG_State::EGLState::EGLContext::IsDmaBufFourccYuv(request.Fourcc)) {
|
||||
@@ -1804,14 +1810,13 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
layout.SitingY = request.SitingY;
|
||||
layout.HasModifier = request.HasModifier;
|
||||
layout.Modifier = request.Modifier;
|
||||
imported = backendObject != nullptr &&
|
||||
backendObject->ImportSharedImagePlanes(request.Fd, static_cast<Uint32>(request.Width),
|
||||
static_cast<Uint32>(request.Height), request.Fourcc,
|
||||
layout, &sharedImageId);
|
||||
} else {
|
||||
imported =
|
||||
backendObject != nullptr &&
|
||||
backendObject->ImportSharedImage(request.Fd, static_cast<Uint32>(request.Width),
|
||||
client != nullptr && client->ImportSharedImagePlanes(request.Fd, static_cast<Uint32>(request.Width),
|
||||
static_cast<Uint32>(request.Height),
|
||||
request.Fourcc, layout, &sharedImageId);
|
||||
} else {
|
||||
imported = client != nullptr &&
|
||||
client->ImportSharedImage(request.Fd, static_cast<Uint32>(request.Width),
|
||||
static_cast<Uint32>(request.Height), request.Fourcc, &sharedImageId);
|
||||
}
|
||||
if (!imported || sharedImageId == 0) {
|
||||
@@ -1831,7 +1836,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
.Height = request.Height,
|
||||
.Fourcc = request.Fourcc,
|
||||
.WireEpoch = wireEpoch});
|
||||
if (image == EGL_NO_IMAGE) (void)backendObject->ReleaseSharedImage(sharedImageId);
|
||||
if (image == EGL_NO_IMAGE) (void)client->ReleaseSharedImage(sharedImageId);
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -1857,8 +1862,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// A texture the image was bound to keeps its own reference on the server (EGLImage
|
||||
// siblings outlive the image), so only this handle's goes.
|
||||
if (sharedImageId != 0) {
|
||||
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
|
||||
(void)backendObject->ReleaseSharedImage(sharedImageId);
|
||||
if (auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get())) {
|
||||
(void)client->ReleaseSharedImage(sharedImageId);
|
||||
}
|
||||
}
|
||||
return EGL_TRUE;
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
EGLBoolean QueryDmaBufFormats(EGLDisplay dpy, EGLint max_formats, EGLint* formats, EGLint* num_formats);
|
||||
EGLBoolean QueryDmaBufModifiers(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR* modifiers,
|
||||
EGLBoolean* external_only, EGLint* num_modifiers);
|
||||
// Whether the active backend has shared images (MG_Backend::BackendObject::AllocateSharedImage):
|
||||
// Whether the active backend has shared images (MG_Backend::ClientHooks::AllocateSharedImage):
|
||||
// a split client's does, a monolith's does not. Decides the dma-buf EGL extensions, the
|
||||
// Wayland linux-dmabuf presentation and GL_OES_EGL_image.
|
||||
Bool SharedImagesAvailable();
|
||||
|
||||
@@ -470,8 +470,8 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
api.marshalFlags(buffer.buffer, kBufferDestroy, nullptr, api.getVersion(buffer.buffer),
|
||||
kMarshalFlagDestroy);
|
||||
if (buffer.imageId != 0) {
|
||||
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
|
||||
(void)backendObject->ReleaseSharedImage(buffer.imageId);
|
||||
if (auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get())) {
|
||||
(void)client->ReleaseSharedImage(buffer.imageId);
|
||||
}
|
||||
}
|
||||
buffer = DmabufBuffer{};
|
||||
@@ -487,15 +487,16 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
Release(buffer);
|
||||
auto* backendObject = MG_Backend::pActiveBackendObject.get();
|
||||
if (protocol == nullptr || dmabuf == nullptr || backendObject == nullptr) return false;
|
||||
MG_Backend::ClientHooks* client = backendObject->AsClient();
|
||||
MG_Backend::SharedImageExport image;
|
||||
if (!backendObject->AllocateSharedImage(static_cast<Uint32>(width), static_cast<Uint32>(height), fourcc,
|
||||
&image)) {
|
||||
if (client == nullptr ||
|
||||
!client->AllocateSharedImage(static_cast<Uint32>(width), static_cast<Uint32>(height), fourcc, &image)) {
|
||||
MGLOG_E("Wayland: the server allocated no %dx%d shared image", width, height);
|
||||
return false;
|
||||
}
|
||||
const auto dropImage = [&] {
|
||||
if (image.Fd >= 0) close(image.Fd);
|
||||
(void)backendObject->ReleaseSharedImage(image.Id);
|
||||
(void)client->ReleaseSharedImage(image.Id);
|
||||
};
|
||||
if (image.Fd < 0 || image.Width != static_cast<Uint32>(width) ||
|
||||
image.Height != static_cast<Uint32>(height)) {
|
||||
@@ -871,7 +872,8 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
const SizeT index = static_cast<SizeT>(buffer - impl.dmabufBuffers);
|
||||
const MG_Util::Damage::Region copy =
|
||||
impl.dmabufDamage.TakeForWrite(index, damage, impl.width, impl.height);
|
||||
if (backendObject->PresentToSharedImage(buffer->imageId, copy)) {
|
||||
MG_Backend::ClientHooks* client = backendObject->AsClient();
|
||||
if (client != nullptr && client->PresentToSharedImage(buffer->imageId, copy)) {
|
||||
impl.Commit(buffer->buffer, damage);
|
||||
buffer->busy = true;
|
||||
return PresentResult::kPresented;
|
||||
|
||||
@@ -6219,9 +6219,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
if (unpackBuffer != 0) BindBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast<GLuint>(unpackBuffer));
|
||||
}
|
||||
|
||||
auto* backendObject = MG_Backend::pActiveBackendObject.get();
|
||||
if (backendObject == nullptr ||
|
||||
!backendObject->AttachSharedImageToTexture(textureObject->GetLifetimeId(), sharedImageId)) {
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
if (client == nullptr || !client->AttachSharedImageToTexture(textureObject->GetLifetimeId(), sharedImageId)) {
|
||||
MGLOG_E_ONCE("glEGLImageTargetTexture2DOES: shared image %llu could not be bound to texture %u",
|
||||
static_cast<unsigned long long>(sharedImageId), textureObject->GetExternalIndex());
|
||||
MG_State::pGLContext->RecordError(
|
||||
|
||||
@@ -249,9 +249,9 @@ namespace MobileGL::MG_Impl::GLXImpl {
|
||||
class BackendImageSource final : public X11Present::ImageSource {
|
||||
public:
|
||||
Bool Allocate(Uint32 width, Uint32 height, Uint32 fourcc, X11Present::Image* out) override {
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
MG_Backend::SharedImageExport image;
|
||||
if (backend == nullptr || !backend->AllocateSharedImage(width, height, fourcc, &image)) return false;
|
||||
if (client == nullptr || !client->AllocateSharedImage(width, height, fourcc, &image)) return false;
|
||||
out->Id = image.Id;
|
||||
out->Fd = image.Fd;
|
||||
out->Width = image.Width;
|
||||
@@ -263,11 +263,12 @@ namespace MobileGL::MG_Impl::GLXImpl {
|
||||
return true;
|
||||
}
|
||||
void Release(Uint64 id) override {
|
||||
if (auto* backend = MG_Backend::pActiveBackendObject.get()) (void)backend->ReleaseSharedImage(id);
|
||||
if (auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get()))
|
||||
(void)client->ReleaseSharedImage(id);
|
||||
}
|
||||
Bool CopyFrame(Uint64 id, const MG_Util::Damage::Region& region) override {
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
return backend != nullptr && backend->PresentToSharedImage(id, region);
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
return client != nullptr && client->PresentToSharedImage(id, region);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -86,8 +86,9 @@ MOBILEGL_EXPORT int mobilegl_shared_image_allocate(uint32_t width, uint32_t heig
|
||||
const std::lock_guard<std::mutex> lock(g_apiMutex);
|
||||
BackendObject* backend = ActiveBackend(true);
|
||||
if (backend == nullptr) return -EIO;
|
||||
MobileGL::MG_Backend::ClientHooks* client = backend->AsClient();
|
||||
MobileGL::MG_Backend::SharedImageExport image{};
|
||||
if (!backend->AllocateSharedImage(width, height, fourcc, &image) || image.Fd < 0) {
|
||||
if (client == nullptr || !client->AllocateSharedImage(width, height, fourcc, &image) || image.Fd < 0) {
|
||||
if (image.Fd >= 0) ::close(image.Fd);
|
||||
return -EIO;
|
||||
}
|
||||
@@ -115,8 +116,9 @@ MOBILEGL_EXPORT int mobilegl_shared_image_import(int fd, uint32_t width, uint32_
|
||||
const std::lock_guard<std::mutex> lock(g_apiMutex);
|
||||
BackendObject* backend = ActiveBackend(true);
|
||||
if (backend == nullptr) return -EIO;
|
||||
MobileGL::MG_Backend::ClientHooks* client = backend->AsClient();
|
||||
uint64_t id = 0;
|
||||
if (!backend->ImportSharedImage(fd, width, height, fourcc, &id)) return -EIO;
|
||||
if (client == nullptr || !client->ImportSharedImage(fd, width, height, fourcc, &id)) return -EIO;
|
||||
*out_id = id;
|
||||
return 0;
|
||||
}
|
||||
@@ -130,6 +132,7 @@ MOBILEGL_EXPORT int mobilegl_shared_image_import_planes(int fd, uint32_t width,
|
||||
const std::lock_guard<std::mutex> lock(g_apiMutex);
|
||||
BackendObject* backend = ActiveBackend(true);
|
||||
if (backend == nullptr) return -EIO;
|
||||
MobileGL::MG_Backend::ClientHooks* client = backend->AsClient();
|
||||
MobileGL::MG_Backend::SharedImageImportLayout layout;
|
||||
layout.PlaneCount = 2;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
@@ -140,7 +143,7 @@ MOBILEGL_EXPORT int mobilegl_shared_image_import_planes(int fd, uint32_t width,
|
||||
layout.HasModifier = planes->modifier != kModifierInvalid;
|
||||
layout.Modifier = planes->modifier;
|
||||
uint64_t id = 0;
|
||||
if (!backend->ImportSharedImagePlanes(fd, width, height, fourcc, layout, &id)) return -EIO;
|
||||
if (client == nullptr || !client->ImportSharedImagePlanes(fd, width, height, fourcc, layout, &id)) return -EIO;
|
||||
*out_id = id;
|
||||
return 0;
|
||||
}
|
||||
@@ -153,5 +156,6 @@ MOBILEGL_EXPORT int mobilegl_shared_image_release(uint64_t id) {
|
||||
const std::lock_guard<std::mutex> lock(g_apiMutex);
|
||||
BackendObject* backend = ActiveBackend(false);
|
||||
if (backend == nullptr) return -ENODEV;
|
||||
return backend->ReleaseSharedImage(id) ? 0 : -EIO;
|
||||
MobileGL::MG_Backend::ClientHooks* client = backend->AsClient();
|
||||
return client != nullptr && client->ReleaseSharedImage(id) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// gen_pipe.py's block regexes end at a blank line: keep the empty line after each macro.
|
||||
// clang-format off
|
||||
|
||||
// X(Verb, Class) - one row per function-pointer member of MG_Backend::GLFunctionsTable (BackendObject.h),
|
||||
// X(Verb, Class) - one row per function-pointer member of MG_Backend::GLFunctionsTable (BackendFunctions.h),
|
||||
// in declaration order. gen_pipe.py parses that struct and refuses a row set that is not exactly its member set.
|
||||
#define MGP_FILL_VERB_LIST(X) \
|
||||
X(DrawArrays, kDraw) \
|
||||
|
||||
@@ -1602,7 +1602,7 @@ namespace MobileGL::MG_Pipe {
|
||||
MGP_ASSERT_POD(MGPStreamOutputBind, 16);
|
||||
|
||||
// set_storage_block_binding = glShaderStorageBlockBinding (P5b i1). The block is named,
|
||||
// not indexed: BackendObject.h says why - the application's index is the frontend
|
||||
// not indexed: BackendFunctions.h says why - the application's index is the frontend
|
||||
// interface-query enumeration's and no backend shares that index space, the NAME is the
|
||||
// one coordinate all three agree on. The name is bytes of unbounded length, so it rides
|
||||
// the ONE client -> server byte carrier, SEG_STAGE, as a kHasBlob blob: Name.Size is
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// (DirectGLES.cpp documents that behaviour where it does the same comparison itself).
|
||||
//
|
||||
// Hand maintained alongside MGPipeTypes.h, MGPipeValueTypes.h, MGPipeHostSpan.h and
|
||||
// MG_Backend/BackendObject.h. Adding a member to one of these structs without adding it here
|
||||
// MG_Backend/BackendFunctions.h. Adding a member to one of these structs without adding it here
|
||||
// would make the comparator blind to it, so gen_pipe.py asserts - in both modes, hence in
|
||||
// pipe-gates - that every list below names exactly the direct data members of its struct
|
||||
// (P1 brief D8; a member named Pad<n> is padding and is not listed).
|
||||
|
||||
@@ -26,11 +26,13 @@
|
||||
|
||||
namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
class BackendObject_Remote final : public MG_Backend::BackendObject {
|
||||
class BackendObject_Remote final : public MG_Backend::BackendObject, public MG_Backend::ClientHooks {
|
||||
public:
|
||||
BackendObject_Remote();
|
||||
~BackendObject_Remote() override;
|
||||
|
||||
MG_Backend::ClientHooks* AsClient() override { return this; }
|
||||
|
||||
// ---- the eight pure virtuals ------------------------------------------------------
|
||||
void Initialize() override;
|
||||
Bool InitCapabilities() override;
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// F1 (P7 wave 2): IT NEVER ANSWERS FROM A PLACEHOLDER ANY MORE. See RequireFirstSnapshot.
|
||||
Bool ServerConsumes(Uint64 subsystemBit) const;
|
||||
|
||||
// GLFunctionsTable::PrefersCpuXfbPrimitiveAccounting (BackendObject.h) does NOT
|
||||
// GLFunctionsTable::PrefersCpuXfbPrimitiveAccounting (BackendFunctions.h) does NOT
|
||||
// ride in MGPCaps::Dynamic - it is a member of the function table, which is precisely
|
||||
// the thing a split client never receives. Its only non-test client reader is
|
||||
// GL_Query.cpp:221, and under split it must be answered from kCapCpuXfbPrimitiveAccounting.
|
||||
|
||||
@@ -509,45 +509,45 @@ namespace MobileGL::MG_Remote::Server {
|
||||
return holder.Release(op.ImageId);
|
||||
case MG_Pipe::kMGPSharedImagePresent: {
|
||||
SI::ImageRef image = holder.Get(op.ImageId);
|
||||
MG_Backend::BackendObject* backend = ServerLoopInstance().Backend();
|
||||
if (image == nullptr || backend == nullptr) return false;
|
||||
MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks();
|
||||
if (image == nullptr || server == nullptr) return false;
|
||||
MG_Backend::SharedImageView view;
|
||||
view.Id = image->Id;
|
||||
view.Width = image->Width;
|
||||
view.Height = image->Height;
|
||||
view.Fourcc = image->Fourcc;
|
||||
view.NativeBuffer = image->Native;
|
||||
if (!backend->BlitDefaultFramebufferToSharedImage(view, DamageOfRecord(op.DamageCount, op.Damage)))
|
||||
if (!server->BlitDefaultFramebufferToSharedImage(view, DamageOfRecord(op.DamageCount, op.Damage)))
|
||||
return false;
|
||||
fill(*image);
|
||||
return true;
|
||||
}
|
||||
case MG_Pipe::kMGPSharedImageQueryBufferAge: {
|
||||
MG_Backend::BackendObject* backend = ServerLoopInstance().Backend();
|
||||
if (backend == nullptr) return false;
|
||||
MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks();
|
||||
if (server == nullptr) return false;
|
||||
reply.BufferAge =
|
||||
backend->QueryCurrentBufferAge((op.Format & MG_Pipe::kMGPBufferAgeDamageRegionFollows) != 0);
|
||||
server->QueryCurrentBufferAge((op.Format & MG_Pipe::kMGPBufferAgeDamageRegionFollows) != 0);
|
||||
return true;
|
||||
}
|
||||
case MG_Pipe::kMGPSharedImageSetDamageRegion: {
|
||||
MG_Backend::BackendObject* backend = ServerLoopInstance().Backend();
|
||||
if (backend == nullptr) return false;
|
||||
return backend->SetCurrentDamageRegion(DamageOfRecord(op.DamageCount, op.Damage));
|
||||
MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks();
|
||||
if (server == nullptr) return false;
|
||||
return server->SetCurrentDamageRegion(DamageOfRecord(op.DamageCount, op.Damage));
|
||||
}
|
||||
case MG_Pipe::kMGPSharedImageFlush: {
|
||||
// The answer is posted after the fence is published: the client commits the buffer to
|
||||
// its compositor only after this returns, so the compositor's session finds the write.
|
||||
MG_Backend::BackendObject* backend = ServerLoopInstance().Backend();
|
||||
if (backend == nullptr) return false;
|
||||
return backend->PublishSharedImageAccesses();
|
||||
MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks();
|
||||
if (server == nullptr) return false;
|
||||
return server->PublishSharedImageAccesses();
|
||||
}
|
||||
case MG_Pipe::kMGPSharedImageNativeFence: {
|
||||
// A descriptor of the session's work, and an image writer's flush (MGPipeTypes.h). Like
|
||||
// an Allocate's, the descriptor goes out BEFORE the reply is posted.
|
||||
MG_Backend::BackendObject* backend = ServerLoopInstance().Backend();
|
||||
if (backend == nullptr) return false;
|
||||
MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks();
|
||||
if (server == nullptr) return false;
|
||||
int fence = -1;
|
||||
if (!backend->ExportNativeFence(&fence)) return false;
|
||||
if (!server->ExportNativeFence(&fence)) return false;
|
||||
#if !defined(_WIN32)
|
||||
if (fence >= 0) {
|
||||
m_lastNativeFence.Reset(::dup(fence));
|
||||
@@ -626,8 +626,8 @@ namespace MobileGL::MG_Remote::Server {
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
(void)MG_Backend::DirectGLES::MakeNativeContextCurrentForBoundToken();
|
||||
// The backend half for one that keeps a single native surface binding (Magma).
|
||||
if (MG_Backend::BackendObject* backend = ServerLoopInstance().Backend()) {
|
||||
backend->OnClientContextBound(bind.ClientContextToken);
|
||||
if (MG_Backend::ServerHooks* server = ServerLoopInstance().BackendHooks()) {
|
||||
server->OnClientContextBound(bind.ClientContextToken);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -767,7 +767,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// Clear -> Clear, DrawVbo -> DrawArrays, ReadPixels -> ReadPixels, Blit -> BlitFramebuffer.
|
||||
//
|
||||
// PRESENT IS DELIBERATELY NOT ONE, although contract 7 puts it in class B: Present and
|
||||
// SetSwapInterval sit outside GLFunctionsTable (BackendObject.h) and read no frontend state,
|
||||
// SetSwapInterval sit outside GLFunctionsTable (BackendFunctions.h) and read no frontend state,
|
||||
// so FillPoints.def's MGP_FILL_VERB_LIST has no row for them. There is no MGPipeVerb::Present,
|
||||
// and stamping there would retire the previous verb's answers with nothing to put in their place.
|
||||
void PipeApplier::StampVerbBoundary(MG_Pipe::MGPWireOp op) {
|
||||
|
||||
@@ -393,6 +393,10 @@ namespace MobileGL::MG_Remote::Server {
|
||||
return m_backend.get();
|
||||
}
|
||||
|
||||
MG_Backend::ServerHooks* ServerLoop::BackendHooks() {
|
||||
return MG_Backend::ServerHooksOf(m_backend.get());
|
||||
}
|
||||
|
||||
MobileGLResult ServerLoop::InstallBackendForTesting(UniquePtr<MG_Backend::BackendObject> backend) {
|
||||
if (m_backend != nullptr || backend == nullptr || m_running.load(std::memory_order_acquire)) {
|
||||
return MOBILEGL_ERR_INVALID_ARGUMENT;
|
||||
@@ -644,9 +648,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
session.PeerSharedSegments() ? "shared segments" : "stream", policy.rule);
|
||||
|
||||
// A served session shares its process with other sessions: its backend's own native state
|
||||
// is bound to this thread before anything can apply a record (BackendObject.h). The inproc
|
||||
// is bound to this thread before anything can apply a record (BackendRoles.h). The inproc
|
||||
// one-process shape has no runtime and keeps the process-wide state.
|
||||
if (m_runtime != nullptr && m_backend != nullptr) m_backend->BindSessionStateToThisThread();
|
||||
if (m_runtime != nullptr && BackendHooks() != nullptr) BackendHooks()->BindSessionStateToThisThread();
|
||||
|
||||
// The decoder is built HERE, on this thread, because PipeWireDecoder is "not thread
|
||||
// safe: one decoder on the apply thread, by construction" and its constructor installs
|
||||
@@ -722,9 +726,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// The head is loaded BEFORE the poll, so every record below it was in the ring when the
|
||||
// poll ran and the drain need not poll for it again (DrainRing's `polledHead`).
|
||||
std::uint64_t polledHead = 0;
|
||||
if (m_backend != nullptr && !SessionLatched()) {
|
||||
if (BackendHooks() != nullptr && !SessionLatched()) {
|
||||
polledHead = signals.CmdHead != nullptr ? signals.CmdHead->load(std::memory_order_acquire) : 0;
|
||||
(void)m_backend->LatchIfGpuFaulted();
|
||||
(void)BackendHooks()->LatchIfGpuFaulted();
|
||||
}
|
||||
if (eventRingHasRoom()) DrainRing(polledHead);
|
||||
if (stopOrForfeit()) break;
|
||||
@@ -1141,7 +1145,8 @@ namespace MobileGL::MG_Remote::Server {
|
||||
record.kind == static_cast<std::uint16_t>(MG_Pipe::MGPWireOp::Present)) {
|
||||
if (unseen) polledHead = signals.CmdHead->load(std::memory_order_acquire);
|
||||
recordsSinceFaultCheck = 0;
|
||||
if (m_backend->LatchIfGpuFaulted()) return false;
|
||||
MG_Backend::ServerHooks* server = BackendHooks();
|
||||
if (server != nullptr && server->LatchIfGpuFaulted()) return false;
|
||||
}
|
||||
}
|
||||
if (SessionLatched()) return false;
|
||||
@@ -2185,7 +2190,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
}
|
||||
|
||||
void ServerLoop::PublishServerWindowResize() {
|
||||
if (!m_holdsWindowLease || m_backend == nullptr) return;
|
||||
if (!m_holdsWindowLease || BackendHooks() == nullptr) return;
|
||||
// THE DISPLAY'S EXTENT, READ NOW: the latest report wins over any a burst of them left queued.
|
||||
void* window = nullptr;
|
||||
Uint32 width = 0;
|
||||
@@ -2197,7 +2202,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
const Uint32 previousWidth = m_publishedWindowWidth;
|
||||
const Uint32 previousHeight = m_publishedWindowHeight;
|
||||
NoteServerWindowExtent(window, width, height);
|
||||
const Bool published = m_backend->ServerWindowResized(window, width, height);
|
||||
const Bool published = BackendHooks()->ServerWindowResized(window, width, height);
|
||||
if (published) m_serverWindowResizes.fetch_add(1, std::memory_order_acq_rel);
|
||||
MGLOG_I("MG_Remote server: server window %p resized %ux%u -> %ux%u; %s", window, previousWidth, previousHeight,
|
||||
width, height,
|
||||
@@ -2228,7 +2233,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// window surfaces, Magma the swapchain and VkSurfaceKHR - keeps its contexts, objects and
|
||||
// device, and the session waits for the display's next window with every record still applied.
|
||||
void* const window = m_leasedWindow;
|
||||
if (m_backend != nullptr && m_backend->SuspendServerWindow(window)) {
|
||||
if (BackendHooks() != nullptr && BackendHooks()->SuspendServerWindow(window)) {
|
||||
// N-3: the native binding moved off the window's surface.
|
||||
ForgetCurrentTuple();
|
||||
m_serverWindowSuspended.store(true, std::memory_order_release);
|
||||
@@ -2277,7 +2282,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
Bool ServerLoop::ResumeServerWindowOnLease(const ServerWindowLease& lease) {
|
||||
// Before the backend: its republished extent is the server window's (MGPipeServerOwnedWindow).
|
||||
NoteServerWindowExtent(lease.window, lease.width, lease.height);
|
||||
if (m_backend == nullptr || !m_backend->ResumeServerWindow(lease.window, lease.width, lease.height)) {
|
||||
if (BackendHooks() == nullptr || !BackendHooks()->ResumeServerWindow(lease.window, lease.width, lease.height)) {
|
||||
LatchServerWindowLost("the suspended on-screen session's surfaces could not be rebuilt on the new server "
|
||||
"window");
|
||||
return false;
|
||||
|
||||
@@ -111,6 +111,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// lookups - take the format cache as a parameter instead. That is six functions across
|
||||
// two files, and it is why no thread-keyed shim is needed for MOBILEGL_BUILD_DISAGGREGATED_INPROC.
|
||||
MG_Backend::BackendObject* Backend();
|
||||
// Its server hooks (BackendRoles.h); null with no backend. Asked at each use, never kept: a
|
||||
// test may swap the backend under a running apply thread.
|
||||
MG_Backend::ServerHooks* BackendHooks();
|
||||
|
||||
// Runs one blocking control request on the apply thread and waits for it; every Server*
|
||||
// EGL forwarder funnels through here. Blocking rather than async, because each caller
|
||||
|
||||
@@ -314,9 +314,9 @@ namespace {
|
||||
return Note(r, "the owner's context did not go current");
|
||||
r.initialized = 1;
|
||||
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
MG_Backend::SharedImageExport image;
|
||||
if (backend == nullptr || !backend->AllocateSharedImage(kImageWidth, kImageHeight, kXrgb8888, &image) ||
|
||||
if (client == nullptr || !client->AllocateSharedImage(kImageWidth, kImageHeight, kXrgb8888, &image) ||
|
||||
image.Fd < 0)
|
||||
return Note(r, "no shared image was allocated");
|
||||
r.allocated = 1;
|
||||
@@ -339,12 +339,12 @@ namespace {
|
||||
r.pixelAfter = ClearAndRead(1.0f, 0.0f, 0.0f);
|
||||
Uint64 again = 0;
|
||||
r.stillIdentified =
|
||||
backend->ImportSharedImage(image.Fd, image.Width, image.Height, image.Fourcc, &again) && again == image.Id
|
||||
client->ImportSharedImage(image.Fd, image.Width, image.Height, image.Fourcc, &again) && again == image.Id
|
||||
? 1
|
||||
: 0;
|
||||
if (again != 0) (void)backend->ReleaseSharedImage(again);
|
||||
if (again != 0) (void)client->ReleaseSharedImage(again);
|
||||
::close(image.Fd);
|
||||
(void)backend->ReleaseSharedImage(image.Id);
|
||||
(void)client->ReleaseSharedImage(image.Id);
|
||||
(void)EGL::MakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
(void)EGL::DestroyContext(dpy, context);
|
||||
(void)EGL::DestroySurface(dpy, surface);
|
||||
|
||||
@@ -163,9 +163,9 @@ namespace {
|
||||
|
||||
// A window pixmap: a shared image (what gbm_bo_create gives glamor), bound through
|
||||
// EGL_LINUX_DMA_BUF_EXT the way xwl_glamor_gbm_create_pixmap_for_bo does.
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
MG_Backend::SharedImageExport image;
|
||||
if (backend != nullptr && backend->AllocateSharedImage(64, 32, kXrgb8888, &image) && image.Fd >= 0) {
|
||||
if (client != nullptr && client->AllocateSharedImage(64, 32, kXrgb8888, &image) && image.Fd >= 0) {
|
||||
const EGLAttrib attribs[] = {EGL_WIDTH,
|
||||
64,
|
||||
EGL_HEIGHT,
|
||||
@@ -183,16 +183,16 @@ namespace {
|
||||
r.imageCreated = eglImage != EGL_NO_IMAGE ? 1 : 0;
|
||||
if (eglImage != EGL_NO_IMAGE) (void)EGL::DestroyImage(dpy, eglImage);
|
||||
::close(image.Fd);
|
||||
(void)backend->ReleaseSharedImage(image.Id);
|
||||
(void)client->ReleaseSharedImage(image.Id);
|
||||
}
|
||||
|
||||
// glamor's block handler: glFlush, which in an X server is where its renders become visible
|
||||
// to the compositor. The verb must be answered (an unknown verb is refused).
|
||||
using ClearFn = void (*)(unsigned);
|
||||
if (const auto clear = reinterpret_cast<ClearFn>(EGL::GetProcAddress("glClear"))) clear(0x4000);
|
||||
r.flushAnswered = backend != nullptr && backend->FlushSharedImageAccesses() ? 1 : 0;
|
||||
r.flushAnswered = client != nullptr && client->FlushSharedImageAccesses() ? 1 : 0;
|
||||
// Nothing reached the stream since: no round trip, still a success.
|
||||
r.flushAgainAnswered = backend != nullptr && backend->FlushSharedImageAccesses() ? 1 : 0;
|
||||
r.flushAgainAnswered = client != nullptr && client->FlushSharedImageAccesses() ? 1 : 0;
|
||||
|
||||
(void)EGL::MakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
(void)EGL::DestroyContext(dpy, ctx);
|
||||
@@ -272,7 +272,7 @@ namespace {
|
||||
|
||||
// A GBM scanout buffer (a shared image), bound as a texture and rendered into through a
|
||||
// framebuffer - and then no glFlush, as Chrome does when it has native fences.
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
MG_Backend::SharedImageExport image;
|
||||
EGLImage eglImage = EGL_NO_IMAGE;
|
||||
using GenFn = void (*)(int, unsigned*);
|
||||
@@ -291,7 +291,7 @@ namespace {
|
||||
const auto checkStatus = reinterpret_cast<StatusFn>(EGL::GetProcAddress("glCheckFramebufferStatus"));
|
||||
const auto clearColor = reinterpret_cast<ClearColorFn>(EGL::GetProcAddress("glClearColor"));
|
||||
const auto clear = reinterpret_cast<ClearFn>(EGL::GetProcAddress("glClear"));
|
||||
if (backend != nullptr && backend->AllocateSharedImage(64, 32, kArgb8888, &image) && image.Fd >= 0) {
|
||||
if (client != nullptr && client->AllocateSharedImage(64, 32, kArgb8888, &image) && image.Fd >= 0) {
|
||||
const EGLAttrib attribs[] = {EGL_WIDTH,
|
||||
64,
|
||||
EGL_HEIGHT,
|
||||
@@ -354,7 +354,7 @@ namespace {
|
||||
|
||||
if (eglImage != EGL_NO_IMAGE) (void)EGL::DestroyImage(dpy, eglImage);
|
||||
if (image.Fd >= 0) ::close(image.Fd);
|
||||
if (backend != nullptr && image.Id != 0) (void)backend->ReleaseSharedImage(image.Id);
|
||||
if (client != nullptr && image.Id != 0) (void)client->ReleaseSharedImage(image.Id);
|
||||
(void)EGL::MakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
(void)EGL::DestroyContext(dpy, ctx);
|
||||
(void)EGL::Terminate(dpy);
|
||||
@@ -399,12 +399,12 @@ namespace {
|
||||
// The GL extension list is where a split client asks the server whether it holds YUV images.
|
||||
r.externalImage = HasGlExtension("GL_OES_EGL_image_external") ? 1 : 0;
|
||||
// Asked anyway (a C-ABI or GLX caller does not look at the extensions first): refused.
|
||||
auto* backend = MG_Backend::pActiveBackendObject.get();
|
||||
auto* client = MG_Backend::ClientHooksOf(MG_Backend::pActiveBackendObject.get());
|
||||
MG_Backend::SharedImageExport image;
|
||||
if (backend != nullptr && backend->AllocateSharedImage(64, 32, kXrgb8888, &image)) {
|
||||
if (client != nullptr && client->AllocateSharedImage(64, 32, kXrgb8888, &image)) {
|
||||
r.allocated = 1;
|
||||
if (image.Fd >= 0) ::close(image.Fd);
|
||||
(void)backend->ReleaseSharedImage(image.Id);
|
||||
(void)client->ReleaseSharedImage(image.Id);
|
||||
}
|
||||
(void)EGL::MakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
(void)EGL::DestroyContext(dpy, ctx);
|
||||
|
||||
@@ -131,8 +131,9 @@ struct ErrorPeer : Codec::ReplySink {
|
||||
}
|
||||
};
|
||||
|
||||
struct CapsPeer : MG_Backend::BackendObject {
|
||||
struct CapsPeer : MG_Backend::BackendObject, MG_Backend::ServerHooks {
|
||||
MG_Backend::DynamicBackendParameters dynamic{};
|
||||
ServerHooks* AsServer() override { return this; }
|
||||
MG_Backend::GlobalBackendFunctionsTable table{};
|
||||
RendererInfo renderer{};
|
||||
void Initialize() override {}
|
||||
|
||||
@@ -3634,7 +3634,7 @@ TEST(ServerLoopLatchTest, ALostServerWindowIsReleasedMidBatchWhileTheClientStrea
|
||||
}
|
||||
|
||||
// =====================================================================================
|
||||
// A SESSION WHOSE OWN GPU WORK HUNG THE GPU APPLIES NOTHING MORE (BackendObject::LatchIfGpuFaulted)
|
||||
// A SESSION WHOSE OWN GPU WORK HUNG THE GPU APPLIES NOTHING MORE (ServerHooks::LatchIfGpuFaulted)
|
||||
// =====================================================================================
|
||||
|
||||
namespace {
|
||||
@@ -3642,9 +3642,10 @@ namespace {
|
||||
// watch's thread (Magma) - while the session's next frame is already queued. The double counts
|
||||
// the frames that reach it and, once `faulted` is set, latches its session from the apply thread
|
||||
// as both real backends do.
|
||||
class GpuFaultBackendDouble final : public MG_Backend::BackendObject {
|
||||
class GpuFaultBackendDouble final : public MG_Backend::BackendObject, public MG_Backend::ServerHooks {
|
||||
public:
|
||||
GpuFaultBackendDouble() { m_functions.Present = &Present; }
|
||||
ServerHooks* AsServer() override { return this; }
|
||||
Bool LatchIfGpuFaulted() override {
|
||||
asked.fetch_add(1, std::memory_order_acq_rel);
|
||||
if (!faulted.load(std::memory_order_acquire)) return false;
|
||||
@@ -3758,10 +3759,10 @@ namespace {
|
||||
// window - presents from then on reach no window - and by taking the new one. Its Present counts
|
||||
// the frames and the ones that came while no window was held. Statics, because the loop owns
|
||||
// (and destroys) the object and each case runs in its own death-test child.
|
||||
class WindowBackendDouble final : public MG_Backend::BackendObject {
|
||||
class WindowBackendDouble final : public MG_Backend::BackendObject, public MG_Backend::ServerHooks {
|
||||
public:
|
||||
explicit WindowBackendDouble(Bool canSuspend) : m_canSuspend(canSuspend) { m_functions.Present = &Present; }
|
||||
|
||||
ServerHooks* AsServer() override { return this; }
|
||||
void Initialize() override {}
|
||||
Bool InitCapabilities() override { return true; }
|
||||
Bool InitWindowSurface() override { return true; }
|
||||
|
||||
Reference in New Issue
Block a user