Skip to content

Commit 691eea9

Browse files
Development/compositor multi client rendering (#949)
* Compositor: Reset _needsModeSet after first commit * Compositor: Add request queue, so requested buffer are always rendered * Compositor: Initialize member variables in Connector constructor * Compositor: Simplify first commit handling in DRM backend * Compositor: Enhance buffer statistics tracking and handling * Compositor: Rename Buffer class to ExternalBuffer and update related references * Compositor: Implement GPU synchronization with Finish() method and update rendering flow * Compositor: Prioritize DRM render node for rendering * Compositor: Deferred texture handling * Compositor: Adding a triple buffer setup to prevent gbm deadlocks. * Compositor: keep the client alive a bit longer * Compositor: Fix ComRPC deadlock when a client crashes.
1 parent 80c155f commit 691eea9

File tree

11 files changed

+777
-298
lines changed

11 files changed

+777
-298
lines changed

Compositor/lib/Mesa/Compositor.cpp

Lines changed: 473 additions & 132 deletions
Large diffs are not rendered by default.

Compositor/lib/Mesa/backend/src/DRM/Atomic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace Compositor {
3030
namespace Backend {
3131
class Transaction {
3232
private:
33-
static constexpr uint32_t DefaultDrmAtomicFlags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
33+
static constexpr uint32_t DefaultDrmAtomicFlags = DRM_MODE_PAGE_FLIP_EVENT;
3434

3535
// Convert integer pixel value to DRM 16.16 fixed-point format
3636
static constexpr uint64_t ToFixedPoint16_16(uint32_t value)

Compositor/lib/Mesa/backend/src/DRM/Connector.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ namespace Compositor {
4242
: _swap()
4343
, _fd(-1)
4444
, _activePlane(~0)
45+
, _buffer()
46+
, _frameId()
47+
, _frameBuffer()
4548
{
4649
// Double buffering: 0=current, 1=next, swap via XOR
4750
_buffer[0] = Core::ProxyType<Exchange::IGraphicsBuffer>();
@@ -129,11 +132,7 @@ namespace Compositor {
129132
}
130133
Compositor::DRM::Identifier Id() const
131134
{
132-
return _frameId[_activePlane]; // Current buffer's frame ID
133-
}
134-
Core::ProxyType<Exchange::IGraphicsBuffer> Buffer() const
135-
{
136-
return _buffer[_activePlane];
135+
return _frameId[_activePlane];
137136
}
138137
void Swap()
139138
{
@@ -183,7 +182,7 @@ namespace Compositor {
183182
TRACE(Trace::Backend, ("Connector %d is not in a valid state", connectorId));
184183
} else {
185184
// Apply scan results
186-
_needsModeSet = scanResult.needsModeSet;
185+
_needsModeSet.store(scanResult.needsModeSet, std::memory_order_relaxed);
187186
_selectedMode = scanResult.selectedMode;
188187
_dimensionsAdjusted = scanResult.dimensionsAdjusted;
189188
_gpuNode = scanResult.gpuNode;
@@ -201,7 +200,7 @@ namespace Compositor {
201200

202201
TRACE(Trace::Information, (_T("Selecting format... ")));
203202

204-
TRACE(Trace::Backend, ("DRM formats: %s", Compositor::ToString(drmFormats).c_str()));
203+
TRACE(Trace::Backend, ("DRM formats: %s", Compositor::ToString(drmFormats).c_str()));
205204
TRACE(Trace::Backend, (_T("Renderer exposes %d render formats. "), renderer->RenderFormats().size()));
206205
TRACE(Trace::Backend, (_T("Backend accepts %d formats."), drmFormats.size()));
207206

@@ -234,7 +233,8 @@ namespace Compositor {
234233

235234
bool NeedsModeSet() const
236235
{
237-
return _needsModeSet;
236+
// no hotplug detection by design.
237+
return _needsModeSet.exchange(false, std::memory_order_acq_rel);
238238
}
239239

240240
const drmModeModeInfo* SelectedMode() const
@@ -335,7 +335,7 @@ namespace Compositor {
335335
}
336336
Core::ProxyType<Compositor::IRenderer::IFrameBuffer> FrameBuffer() const override
337337
{
338-
return (_frameBuffer.IsValid() == true) ? _frameBuffer.FrameBuffer() : Core::ProxyType<Compositor::IRenderer::IFrameBuffer>();
338+
return (_frameBuffer.IsValid() == true) ? _frameBuffer.FrameBuffer() : Core::ProxyType<Compositor::IRenderer::IFrameBuffer>();
339339
}
340340

341341
private:
@@ -350,7 +350,7 @@ namespace Compositor {
350350

351351
Compositor::IOutput::ICallback* _feedback;
352352

353-
bool _needsModeSet;
353+
mutable std::atomic<bool> _needsModeSet;
354354
drmModeModeInfo _selectedMode;
355355
bool _dimensionsAdjusted;
356356
};

Compositor/lib/Mesa/backend/src/DRM/DRM.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,11 @@ namespace Compositor {
201201
_connectors.Visit([&](const string& /*name*/, const Core::ProxyType<Connector> connector) {
202202
connector->Presented(0, 0); // notify connector implementation the buffer failed to display.
203203
});
204+
} else {
205+
if (doModeSet) {
206+
_firstCommit = false;
207+
}
204208
}
205-
206-
if (doModeSet) {
207-
_firstCommit = false;
208-
}
209-
210-
TRACE_GLOBAL(Trace::Information, ("Committed connectors: %u", result));
211209
} else {
212210
TRACE_GLOBAL(Trace::Information, ("Commit in progress, skipping commit request"));
213211
result = Core::ERROR_INPROGRESS;
@@ -228,8 +226,6 @@ namespace Compositor {
228226
presentationTimestamp.tv_nsec = useconds * 1000;
229227

230228
connector->Presented(sequence, Core::Time(presentationTimestamp).Ticks());
231-
// TODO: Check with Bram the intention of the Release()
232-
// connector->Release();
233229

234230
TRACE(Trace::Backend, ("Pageflip finished for %s", name.c_str()));
235231
}

Compositor/lib/Mesa/include/IRenderer.h

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace Compositor {
2929
/**
3030
* @brief A frame buffer represents an image used as a target for rendering.
3131
*/
32-
struct IFrameBuffer{
32+
struct IFrameBuffer {
3333
virtual ~IFrameBuffer() = default;
3434

3535
virtual bool IsValid() const = 0;
@@ -42,10 +42,13 @@ namespace Compositor {
4242
* @brief A texture represents an image used as a source for rendering.
4343
*/
4444
struct ITexture {
45+
using Id = uint32_t;
46+
4547
virtual ~ITexture() = default;
4648

4749
virtual bool IsValid() const = 0;
4850

51+
virtual Id Identifier() const = 0;
4952
virtual uint32_t Width() const = 0;
5053
virtual uint32_t Height() const = 0;
5154
}; // struct ITexture
@@ -72,23 +75,35 @@ namespace Compositor {
7275
virtual uint32_t Unbind(const Core::ProxyType<IFrameBuffer>& framebuffer) = 0;
7376

7477
/**
75-
* @brief Start a render pass with the provided viewport.
76-
*
77-
* This should be called after a binding a buffer, callee must call
78-
* End() when they are done rendering.
78+
* @brief Begins recording rendering commands for the current frame.
7979
*
8080
* @param width Viewport width in pixels
8181
* @param height Viewport height in pixels
82-
* @return false on failure, in which case compositors shouldn't try rendering.
82+
* @return false on failure
8383
*/
8484
virtual bool Begin(uint32_t width, uint32_t height) = 0;
8585

8686
/**
87-
* @brief Ends a render pass.
87+
* @brief Ends recording rendering commands for the current frame.
88+
*
89+
* After calling End(), the command buffer is closed but NOT yet submitted to GPU.
90+
* Call Finish() to submit and wait for GPU completion.
8891
*
92+
* @param dump If true, captures a snapshot of the rendered frame
8993
*/
9094
virtual void End(bool dump = false) = 0;
9195

96+
/**
97+
* @brief Submits recorded commands to GPU and waits for completion.
98+
*
99+
* This call blocks until all rendering commands have finished executing on the GPU.
100+
* Must be called after End() and before committing the frame buffer to display hardware.
101+
*
102+
* @param timeoutMs Maximum time to wait in milliseconds (default: 100ms)
103+
* @return uint32_t Core::ERROR_NONE if rendering completed, Core::ERROR_TIMEDOUT on timeout
104+
*/
105+
virtual uint32_t Finish(uint32_t timeoutMs = 100) = 0;
106+
92107
/**
93108
* @brief Clear the viewport with the provided color
94109
*
@@ -130,7 +145,10 @@ namespace Compositor {
130145
* @return A proxy to the ITexture representing the texture derived from
131146
* the provided composition buffer.
132147
*/
133-
virtual Core::ProxyType<ITexture> Texture(const Core::ProxyType<Exchange::IGraphicsBuffer>& buffer) = 0;
148+
virtual Core::ProxyType<ITexture> CreateTexture(const Core::ProxyType<Exchange::IGraphicsBuffer>& buffer) = 0;
149+
150+
151+
virtual void DestroyTexture(ITexture::Id id) = 0;
134152

135153
/**
136154
* @brief Renders a texture on the bound buffer at the given region with
@@ -144,7 +162,7 @@ namespace Compositor {
144162
*
145163
* @return uint32_t Core::ERROR_NONE if all went ok, error code otherwise.
146164
*/
147-
virtual uint32_t Render(const Core::ProxyType<ITexture>& texture, const Exchange::IComposition::Rectangle& region, const Matrix transform, float alpha) = 0;
165+
virtual uint32_t Render(const ITexture::Id textureId, const Exchange::IComposition::Rectangle& region, const Matrix transform, float alpha) = 0;
148166

149167
/**
150168
* @brief Renders a solid quadrangle* in the specified color with the specified matrix.

Compositor/lib/Mesa/renderer/src/GL/EGL.cpp

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -295,57 +295,71 @@ namespace Compositor {
295295

296296
EGLDeviceEXT* eglDevices = static_cast<EGLDeviceEXT*>(ALLOCA(nEglDevices * sizeof(EGLDeviceEXT)));
297297

298-
memset(eglDevices, 0, sizeof(nEglDevices * sizeof(EGLDeviceEXT)));
298+
// FIXED: memset was wrong
299+
memset(eglDevices, 0, nEglDevices * sizeof(EGLDeviceEXT));
299300

300301
_api.eglQueryDevicesEXT(nEglDevices, eglDevices, &nEglDevices);
301302

302303
if (nEglDevices > 0) {
303-
drmDevice* drmDevice = nullptr;
304-
int ret = drmGetDevice(drmFd, &drmDevice);
304+
drmDevice* drmDev = nullptr;
305+
int ret = drmGetDevice(drmFd, &drmDev);
305306

306-
if ((ret == 0) && (drmDevice != nullptr)) {
307+
if ((ret == 0) && (drmDev != nullptr)) {
307308
for (int i = 0; i < nEglDevices; i++) {
308-
TRACE(Trace::EGL, ("Trying device %d [%p]", i, eglDevices[i]));
309+
310+
EGLDeviceEXT dev = eglDevices[i];
311+
TRACE(Trace::EGL, ("Trying device %d [%p]", i, dev));
309312

310313
#ifdef __DEBUG__
311-
const char* extensions = _api.eglQueryDeviceStringEXT(eglDevices[i], EGL_EXTENSIONS);
314+
const char* extensions = _api.eglQueryDeviceStringEXT(dev, EGL_EXTENSIONS);
312315
if (extensions != nullptr) {
313316
TRACE(Trace::EGL, ("EGL extensions: %s", extensions));
314317
}
315318
#endif
316-
const char* renderName = _api.eglQueryDeviceStringEXT(eglDevices[i], EGL_DRM_RENDER_NODE_FILE_EXT);
317319

318-
if (renderName != nullptr) {
320+
const char* renderName = _api.eglQueryDeviceStringEXT(dev, EGL_DRM_RENDER_NODE_FILE_EXT);
321+
322+
const char* deviceName = _api.eglQueryDeviceStringEXT(dev, EGL_DRM_DEVICE_FILE_EXT);
323+
324+
if (renderName)
319325
TRACE(Trace::EGL, ("EGL render node: %s", renderName));
320-
} else {
321-
TRACE(Trace::Error, ("No EGL render node associated to %p", eglDevices[i]));
326+
else
327+
TRACE(Trace::Error, ("No EGL render node associated to %p", dev));
328+
329+
if (deviceName)
330+
TRACE(Trace::EGL, ("Found EGL device %s", deviceName));
331+
else {
332+
TRACE(Trace::Error, ("No EGL device name returned for %p", dev));
333+
continue;
322334
}
323335

324-
const char* deviceName = _api.eglQueryDeviceStringEXT(eglDevices[i], EGL_DRM_DEVICE_FILE_EXT);
325-
bool nodePresent(false);
336+
bool renderMatch = false;
337+
bool primaryMatch = false;
326338

327-
if (deviceName == nullptr) {
328-
TRACE(Trace::Error, ("No EGL device name returned for %p", eglDevices[i]));
329-
continue;
330-
} else {
331-
TRACE(Trace::EGL, ("Found EGL device %s", deviceName));
339+
// Prefer matching render node
340+
if (renderName && (drmDev->available_nodes & (1 << DRM_NODE_RENDER)) && strcmp(drmDev->nodes[DRM_NODE_RENDER], renderName) == 0) {
341+
renderMatch = true;
342+
}
332343

333-
for (uint16_t i = 0; i < DRM_NODE_MAX; i++) {
334-
if ((drmDevice->available_nodes & (1 << i)) && (strcmp(drmDevice->nodes[i], deviceName) == 0)) {
335-
nodePresent = true;
336-
break;
337-
}
338-
}
344+
// Then fallback to primary node
345+
if (deviceName && (drmDev->available_nodes & (1 << DRM_NODE_PRIMARY)) && strcmp(drmDev->nodes[DRM_NODE_PRIMARY], deviceName) == 0) {
346+
primaryMatch = true;
347+
}
348+
349+
if (renderMatch) {
350+
TRACE(Trace::EGL, ("Using EGL device (render node): %s", renderName));
351+
result = dev;
352+
break;
339353
}
340354

341-
if ((deviceName != nullptr) && (nodePresent == true)) {
342-
TRACE(Trace::EGL, ("Using EGL device %s", deviceName));
343-
result = eglDevices[i];
355+
if (primaryMatch) {
356+
TRACE(Trace::EGL, ("Using EGL device (primary node fallback): %s", deviceName));
357+
result = dev;
344358
break;
345359
}
346360
}
347361

348-
drmFreeDevice(&drmDevice);
362+
drmFreeDevice(&drmDev);
349363
} else {
350364
TRACE(Trace::Error, ("No DRM devices found"));
351365
}

Compositor/lib/Mesa/renderer/src/GL/EGL.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,51 @@ namespace Compositor {
122122
EGLSurface _readSurface;
123123
};
124124

125+
void DestroySync(EGLSync& sync) const
126+
{
127+
if (sync != EGL_NO_SYNC && _api.eglDestroySync != nullptr) {
128+
_api.eglDestroySync(_display, sync);
129+
sync = EGL_NO_SYNC;
130+
}
131+
}
132+
133+
EGLSync CreateSync() const
134+
{
135+
EGLSync sync = EGL_NO_SYNC;
136+
137+
if (_api.eglCreateSync != nullptr) {
138+
sync = _api.eglCreateSync(_display, EGL_SYNC_FENCE, nullptr);
139+
}
140+
141+
return sync;
142+
}
143+
144+
EGLint WaitSync(EGLSync& sync, const EGLTime timeoutNs = EGL_FOREVER) const
145+
{
146+
EGLint status = EGL_UNSIGNALED;
147+
148+
if (sync != EGL_NO_SYNC && _api.eglWaitSync != nullptr) {
149+
status = _api.eglClientWaitSync(_display, sync, EGL_SYNC_FLUSH_COMMANDS_BIT, timeoutNs);
150+
}
151+
152+
return status;
153+
}
154+
155+
int ExportSyncAsFd(EGLSync sync) const
156+
{
157+
int fd = -1;
158+
159+
if (sync != EGL_NO_SYNC && _api.eglDupNativeFenceFDANDROID != nullptr) {
160+
fd = _api.eglDupNativeFenceFDANDROID(_display, sync);
161+
162+
if (fd < 0) {
163+
TRACE(Trace::Error, ("Failed to export sync as fd: 0x%x", eglGetError()));
164+
}
165+
}
166+
167+
return fd;
168+
}
169+
125170
private:
126171
EGLDeviceEXT FindEGLDevice(const int drmFd);
127172
uint32_t Initialize(EGLenum platform, void* remote_display, bool isMaster);

0 commit comments

Comments
 (0)