1 /*
2 * Copyright (C) 2020 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
16 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "egl-client-dmabuf-pool.h"
27
28 #include "ws-client.h"
29
30 #include <array>
31 #include <cstdio>
32
33 namespace WS {
34 namespace EGLClient {
35
BackendDmabufPool(BaseBackend &)36 BackendDmabufPool::BackendDmabufPool(BaseBackend&)
37 {
38 }
39
40 BackendDmabufPool::~BackendDmabufPool() = default;
41
nativeDisplay() const42 EGLNativeDisplayType BackendDmabufPool::nativeDisplay() const
43 {
44 return EGL_DEFAULT_DISPLAY;
45 }
46
platform() const47 uint32_t BackendDmabufPool::platform() const
48 {
49 return EGL_PLATFORM_SURFACELESS_MESA;
50 }
51
52
53 struct TargetDmabufPool::Buffer {
54 struct wl_list link;
55 struct wl_buffer* buffer { nullptr };
56 bool locked { false };
57
58 struct {
59 uint32_t width;
60 uint32_t height;
61 uint32_t format;
62 } meta;
63
64 struct {
65 EGLImageKHR image;
66 } egl;
67
68 struct {
69 GLuint colorBuffer;
70 GLuint dsBuffer;
71 } gl;
72 };
73
74 struct BufferData {
75 bool complete { false };
76
77 uint32_t width { 0 };
78 uint32_t height { 0 };
79 uint32_t format { 0 };
80
81 uint32_t numPlanes { 0 };
82 std::array<int, 4> fds { -1, -1, -1, -1 };
83 std::array<uint32_t, 4> strides { };
84 std::array<uint32_t, 4> offsets { };
85 std::array<uint64_t, 4> modifiers { };
86 };
87
TargetDmabufPool(BaseTarget & base,uint32_t width,uint32_t height)88 TargetDmabufPool::TargetDmabufPool(BaseTarget& base, uint32_t width, uint32_t height)
89 : m_base(base)
90 {
91 wl_list_init(&m_buffer.list);
92
93 m_renderer.width = width;
94 m_renderer.height = height;
95 }
96
97 TargetDmabufPool::~TargetDmabufPool() = default;
98
nativeWindow() const99 EGLNativeWindowType TargetDmabufPool::nativeWindow() const
100 {
101 return 0;
102 }
103
resize(uint32_t width,uint32_t height)104 void TargetDmabufPool::resize(uint32_t width, uint32_t height)
105 {
106 if (m_renderer.width == width && m_renderer.height == height)
107 return;
108
109 m_renderer.width = width;
110 m_renderer.height = height;
111
112 m_buffer.current = nullptr;
113
114 Buffer* buffer;
115 Buffer* tmp;
116 wl_list_for_each_safe(buffer, tmp, &m_buffer.list, link) {
117 wl_list_remove(&buffer->link);
118 destroyBuffer(buffer);
119 }
120 wl_list_init(&m_buffer.list);
121 }
122
frameWillRender()123 void TargetDmabufPool::frameWillRender()
124 {
125 if (!m_renderer.initialized) {
126 m_renderer.initialized = true;
127
128 m_renderer.createImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(
129 eglGetProcAddress("eglCreateImageKHR"));
130 m_renderer.destroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(
131 eglGetProcAddress("eglDestroyImageKHR"));
132 m_renderer.imageTargetRenderbufferStorageOES = reinterpret_cast<PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC>(
133 eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES"));
134
135 GLuint framebuffer { 0 };
136 glGenFramebuffers(1, &framebuffer);
137 m_renderer.framebuffer = framebuffer;
138 }
139
140 m_base.requestFrame();
141
142 g_assert(!m_buffer.current);
143 {
144 Buffer* b;
145 wl_list_for_each(b, &m_buffer.list, link) {
146 if (b->locked)
147 continue;
148
149 m_buffer.current = b;
150 break;
151 }
152 }
153 if (!m_buffer.current) {
154 auto* buffer = new Buffer;
155 buffer->buffer = wpe_dmabuf_pool_create_buffer(m_base.wpeDmabufPool(), m_renderer.width, m_renderer.height);
156 wl_buffer_add_listener(buffer->buffer, &s_bufferListener, this);
157
158 wl_list_insert(&m_buffer.list, &buffer->link);
159 m_buffer.current = buffer;
160
161 struct wpe_dmabuf_data* dmabufData = wpe_dmabuf_pool_get_dmabuf_data(m_base.wpeDmabufPool(), buffer->buffer);
162 wl_proxy_set_queue(reinterpret_cast<struct wl_proxy*>(dmabufData), m_base.eventQueue());
163
164 BufferData bufferData;
165 wpe_dmabuf_data_add_listener(dmabufData, &s_dmabufDataListener, &bufferData);
166 wpe_dmabuf_data_request(dmabufData);
167 wl_display_roundtrip_queue(m_base.display(), m_base.eventQueue());
168
169 buffer->meta.width = bufferData.width;
170 buffer->meta.height = bufferData.height;
171 buffer->meta.format = bufferData.format;
172
173 std::array<EGLint, 64> attributes;
174 {
175 attributes[0] = EGL_WIDTH; attributes[1] = bufferData.width;
176 attributes[2] = EGL_HEIGHT; attributes[3] = bufferData.height;
177 attributes[4] = EGL_LINUX_DRM_FOURCC_EXT; attributes[5] = bufferData.format;
178 unsigned attributesCount = 6;
179
180 if (bufferData.numPlanes >= 1) {
181 uint32_t modifier_hi = bufferData.modifiers[0] >> 32;
182 uint32_t modifier_lo = bufferData.modifiers[0] & 0xFFFFFFFF;
183 std::array<EGLAttrib, 10> planeAttributes = {
184 EGL_DMA_BUF_PLANE0_FD_EXT, bufferData.fds[0],
185 EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast<int>(bufferData.strides[0]),
186 EGL_DMA_BUF_PLANE0_OFFSET_EXT, static_cast<int>(bufferData.offsets[0]),
187 EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, static_cast<int>(modifier_hi),
188 EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, static_cast<int>(modifier_lo),
189 };
190
191 std::copy(planeAttributes.begin(), planeAttributes.end(),
192 std::next(attributes.begin(), attributesCount));
193 attributesCount += planeAttributes.size();
194 }
195
196 if (bufferData.numPlanes >= 2) {
197 uint32_t modifier_hi = bufferData.modifiers[1] >> 32;
198 uint32_t modifier_lo = bufferData.modifiers[1] & 0xFFFFFFFF;
199 std::array<EGLAttrib, 10> planeAttributes = {
200 EGL_DMA_BUF_PLANE1_FD_EXT, bufferData.fds[1],
201 EGL_DMA_BUF_PLANE1_PITCH_EXT, static_cast<int>(bufferData.strides[1]),
202 EGL_DMA_BUF_PLANE1_OFFSET_EXT, static_cast<int>(bufferData.offsets[1]),
203 EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, static_cast<int>(modifier_hi),
204 EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, static_cast<int>(modifier_lo),
205 };
206
207 std::copy(planeAttributes.begin(), planeAttributes.end(),
208 std::next(attributes.begin(), attributesCount));
209 attributesCount += planeAttributes.size();
210 }
211
212 if (bufferData.numPlanes >= 3) {
213 uint32_t modifier_hi = bufferData.modifiers[2] >> 32;
214 uint32_t modifier_lo = bufferData.modifiers[2] & 0xFFFFFFFF;
215 std::array<EGLAttrib, 10> planeAttributes = {
216 EGL_DMA_BUF_PLANE2_FD_EXT, bufferData.fds[2],
217 EGL_DMA_BUF_PLANE2_PITCH_EXT, static_cast<int>(bufferData.strides[2]),
218 EGL_DMA_BUF_PLANE2_OFFSET_EXT, static_cast<int>(bufferData.offsets[2]),
219 EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, static_cast<int>(modifier_hi),
220 EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, static_cast<int>(modifier_lo),
221 };
222
223 std::copy(planeAttributes.begin(), planeAttributes.end(),
224 std::next(attributes.begin(), attributesCount));
225 attributesCount += planeAttributes.size();
226 }
227
228 if (bufferData.numPlanes >= 4) {
229 uint32_t modifier_hi = bufferData.modifiers[3] >> 32;
230 uint32_t modifier_lo = bufferData.modifiers[3] & 0xFFFFFFFF;
231 std::array<EGLAttrib, 10> planeAttributes = {
232 EGL_DMA_BUF_PLANE3_FD_EXT, bufferData.fds[3],
233 EGL_DMA_BUF_PLANE3_PITCH_EXT, static_cast<int>(bufferData.strides[3]),
234 EGL_DMA_BUF_PLANE3_OFFSET_EXT, static_cast<int>(bufferData.offsets[3]),
235 EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, static_cast<int>(modifier_hi),
236 EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, static_cast<int>(modifier_lo),
237 };
238
239 std::copy(planeAttributes.begin(), planeAttributes.end(),
240 std::next(attributes.begin(), attributesCount));
241 attributesCount += planeAttributes.size();
242 }
243 attributes[attributesCount++] = EGL_NONE;
244 }
245
246 buffer->egl.image = m_renderer.createImageKHR(eglGetCurrentDisplay(),
247 EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attributes.data());
248
249 for (unsigned i = 0; i < bufferData.numPlanes; ++i) {
250 if (bufferData.fds[i] != -1)
251 close(bufferData.fds[i]);
252 }
253
254 if (buffer->egl.image == EGL_NO_IMAGE_KHR) {
255 g_warning("unable to create EGLImage from the dma-buf data, error %x", eglGetError());
256 return;
257 }
258
259 std::array<GLuint, 2> renderbuffers { 0, 0 };
260 glGenRenderbuffers(2, renderbuffers.data());
261 buffer->gl.colorBuffer = renderbuffers[0];
262 buffer->gl.dsBuffer = renderbuffers[1];
263
264 glBindRenderbuffer(GL_RENDERBUFFER, buffer->gl.colorBuffer);
265 m_renderer.imageTargetRenderbufferStorageOES(GL_RENDERBUFFER, buffer->egl.image);
266
267 glBindRenderbuffer(GL_RENDERBUFFER, buffer->gl.dsBuffer);
268 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, buffer->meta.width, buffer->meta.height);
269 }
270
271 glBindFramebuffer(GL_FRAMEBUFFER, m_renderer.framebuffer);
272 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
273 GL_RENDERBUFFER, m_buffer.current->gl.colorBuffer);
274 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
275 GL_RENDERBUFFER, m_buffer.current->gl.dsBuffer);
276 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
277 GL_RENDERBUFFER, m_buffer.current->gl.dsBuffer);
278
279 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
280 g_warning("established framebuffer object is not framebuffer-complete");
281 }
282
frameRendered()283 void TargetDmabufPool::frameRendered()
284 {
285 glFlush();
286
287 wl_surface_attach(m_base.surface(), m_buffer.current->buffer, 0, 0);
288 wl_surface_commit(m_base.surface());
289
290 m_buffer.current->locked = true;
291 m_buffer.current = nullptr;
292 }
293
deinitialize()294 void TargetDmabufPool::deinitialize()
295 {
296 m_buffer.current = nullptr;
297
298 Buffer* buffer;
299 Buffer* tmp;
300 wl_list_for_each_safe(buffer, tmp, &m_buffer.list, link) {
301 wl_list_remove(&buffer->link);
302 destroyBuffer(buffer);
303 }
304 wl_list_init(&m_buffer.list);
305
306 if (m_renderer.framebuffer) {
307 glDeleteFramebuffers(1, &m_renderer.framebuffer);
308 m_renderer.framebuffer = 0;
309 }
310 }
311
destroyBuffer(Buffer * buffer)312 void TargetDmabufPool::destroyBuffer(Buffer* buffer)
313 {
314 auto& b = *buffer;
315 g_clear_pointer(&b.buffer, wl_buffer_destroy);
316 if (b.gl.colorBuffer)
317 glDeleteRenderbuffers(1, &b.gl.colorBuffer);
318 if (b.gl.dsBuffer)
319 glDeleteRenderbuffers(1, &b.gl.dsBuffer);
320 if (b.egl.image)
321 m_renderer.destroyImageKHR(eglGetCurrentDisplay(), b.egl.image);
322
323 delete buffer;
324 }
325
326 const struct wpe_dmabuf_data_listener TargetDmabufPool::s_dmabufDataListener = {
327 // atributes
328 [](void* data, struct wpe_dmabuf_data*, uint32_t width, uint32_t height, uint32_t format, uint32_t num_planes)
__anon51e76eac0402(void* data, struct wpe_dmabuf_data*, uint32_t width, uint32_t height, uint32_t format, uint32_t num_planes) 329 {
330 auto& bufferData = *static_cast<BufferData*>(data);
331 bufferData.width = width;
332 bufferData.height = height;
333 bufferData.format = format;
334 bufferData.numPlanes = num_planes;
335 },
336 // plane
337 [](void* data, struct wpe_dmabuf_data*, uint32_t id, int32_t fd, uint32_t stride, uint32_t offset, uint32_t modifier_hi, uint32_t modifier_lo)
__anon51e76eac0502(void* data, struct wpe_dmabuf_data*, uint32_t id, int32_t fd, uint32_t stride, uint32_t offset, uint32_t modifier_hi, uint32_t modifier_lo) 338 {
339 auto& bufferData = *static_cast<BufferData*>(data);
340 bufferData.fds[id] = fd;
341 bufferData.strides[id] = stride;
342 bufferData.offsets[id] = offset;
343 bufferData.modifiers[id] = (uint64_t(modifier_hi) << 32) | uint64_t(modifier_lo);
344 },
345 // done
346 [](void* data, struct wpe_dmabuf_data*)
__anon51e76eac0602(void* data, struct wpe_dmabuf_data*) 347 {
348 auto& bufferData = *static_cast<BufferData*>(data);
349 bufferData.complete = true;
350 },
351 };
352
353 const struct wl_buffer_listener TargetDmabufPool::s_bufferListener = {
354 // release
355 [](void* data, struct wl_buffer* buffer)
__anon51e76eac0702(void* data, struct wl_buffer* buffer) 356 {
357 auto& target = *static_cast<TargetDmabufPool*>(data);
358
359 Buffer* b;
360 wl_list_for_each(b, &target.m_buffer.list, link) {
361 if (buffer == b->buffer) {
362 b->locked = false;
363 break;
364 }
365 }
366 }
367 };
368
369 } } // namespace WS::EGLClient
370