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