buffer_interface.cpp 8.67 KB
Newer Older
1
2
/*
    SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3

4
5
    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
6
#include "buffer_interface.h"
7
#include "compositor_interface.h"
8
#include "display.h"
9
#include "logging.h"
Roman Gilg's avatar
Roman Gilg committed
10
#include "linuxdmabuf_v1_interface.h"
11
12
// Wayland
#include <wayland-server.h>
13
14
15
// EGL
#include <EGL/egl.h>
#include <QtGui/qopengl.h>
16

Roman Gilg's avatar
Roman Gilg committed
17
18
#include "drm_fourcc.h"

19
namespace KWaylandServer
20
21
{

22
23
24
25
26
27
namespace EGL
{
typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr;
}

28
29
30
class BufferInterface::Private
{
public:
31
    Private(BufferInterface *q, Display *display, wl_resource *resource);
32
    ~Private();
33
    QImage::Format format() const;
34
    QImage createImage();
35
36
    wl_resource *buffer;
    wl_shm_buffer *shmBuffer;
Roman Gilg's avatar
Roman Gilg committed
37
    LinuxDmabufBuffer *dmabufBuffer;
38
    int refCount;
39
    QSize size;
40
    bool alpha;
41

42
43
    static BufferInterface *get(wl_resource *r);

44
45
46
private:
    static void destroyListenerCallback(wl_listener *listener, void *data);
    static Private *cast(wl_resource *r);
47
    static void imageBufferCleanupHandler(void *info);
48
    static QList<Private*> s_buffers;
49
50
    static Private *s_accessedBuffer;
    static int s_accessCounter;
51
52
53

    BufferInterface *q;
    wl_listener listener;
54
55
};

56
QList<BufferInterface::Private*> BufferInterface::Private::s_buffers;
57
58
BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr;
int BufferInterface::Private::s_accessCounter = 0;
59
60
61
62
63
64
65
66
67
68

BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r)
{
    auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) { return d->buffer == r; });
    if (it == s_buffers.constEnd()) {
        return nullptr;
    }
    return *it;
}

69
70
71
72
73
74
75
76
77
BufferInterface *BufferInterface::Private::get(wl_resource *r)
{
    Private *p = cast(r);
    if (!p) {
        return nullptr;
    }
    return p->q;
}

78
79
80
81
82
83
84
85
86
87
88
89
void BufferInterface::Private::imageBufferCleanupHandler(void *info)
{
    Private *p = reinterpret_cast<Private*>(info);
    Q_ASSERT(p == s_accessedBuffer);
    Q_ASSERT(s_accessCounter > 0);
    s_accessCounter--;
    if (s_accessCounter == 0) {
        s_accessedBuffer = nullptr;
    }
    wl_shm_buffer_end_access(p->shmBuffer);
}

90
BufferInterface::Private::Private(BufferInterface *q, Display *display, wl_resource *resource)
91
92
    : buffer(resource)
    , shmBuffer(wl_shm_buffer_get(resource))
Roman Gilg's avatar
Roman Gilg committed
93
    , dmabufBuffer(nullptr)
94
    , refCount(0)
95
    , alpha(false)
96
    , q(q)
97
{
Roman Gilg's avatar
Roman Gilg committed
98
99
100
    if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) {
        dmabufBuffer = static_cast<LinuxDmabufBuffer *>(wl_resource_get_user_data(resource));
    }
101
102
    s_buffers << this;
    listener.notify = destroyListenerCallback;
103
104
    listener.link.prev = nullptr;
    listener.link.next = nullptr;
105
    wl_resource_add_destroy_listener(resource, &listener);
106
107
    if (shmBuffer) {
        size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer));
108
109
110
111
112
113
114
115
116
117
        // check alpha
        switch (wl_shm_buffer_get_format(shmBuffer)) {
        case WL_SHM_FORMAT_ARGB8888:
            alpha = true;
            break;
        case WL_SHM_FORMAT_XRGB8888:
        default:
            alpha = false;
            break;
        }
Roman Gilg's avatar
Roman Gilg committed
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    } else if (dmabufBuffer) {
        switch (dmabufBuffer->format()) {
        case DRM_FORMAT_ARGB4444:
        case DRM_FORMAT_ABGR4444:
        case DRM_FORMAT_RGBA4444:
        case DRM_FORMAT_BGRA4444:

        case DRM_FORMAT_ARGB1555:
        case DRM_FORMAT_ABGR1555:
        case DRM_FORMAT_RGBA5551:
        case DRM_FORMAT_BGRA5551:

        case DRM_FORMAT_ARGB8888:
        case DRM_FORMAT_ABGR8888:
        case DRM_FORMAT_RGBA8888:
        case DRM_FORMAT_BGRA8888:

        case DRM_FORMAT_ARGB2101010:
        case DRM_FORMAT_ABGR2101010:
        case DRM_FORMAT_RGBA1010102:
        case DRM_FORMAT_BGRA1010102:

        case DRM_FORMAT_XRGB8888_A8:
        case DRM_FORMAT_XBGR8888_A8:
        case DRM_FORMAT_RGBX8888_A8:
        case DRM_FORMAT_BGRX8888_A8:
        case DRM_FORMAT_RGB888_A8:
        case DRM_FORMAT_BGR888_A8:
        case DRM_FORMAT_RGB565_A8:
        case DRM_FORMAT_BGR565_A8:
            alpha = true;
            break;
        default:
            alpha = false;
            break;
        }
        size = dmabufBuffer->size();
155
156
    } else {
        EGLDisplay eglDisplay = display->eglDisplay();
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        static bool resolved = false;
        using namespace EGL;
        if (!resolved && eglDisplay != EGL_NO_DISPLAY) {
            eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL");
            resolved = true;
        }
        if (eglQueryWaylandBufferWL) {
            EGLint width, height;
            bool valid = false;
            valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width);
            valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height);
            if (valid) {
                size = QSize(width, height);
            }
171
172
173
174
175
176
177
178
179
180
181
182
183
            // check alpha
            EGLint format;
            if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) {
                switch (format) {
                case EGL_TEXTURE_RGBA:
                    alpha = true;
                    break;
                case EGL_TEXTURE_RGB:
                default:
                    alpha = false;
                    break;
                }
            }
184
        }
185
    }
186
187
}

188
189
BufferInterface::Private::~Private()
{
190
    wl_list_remove(&listener.link);
191
192
    s_buffers.removeAll(this);
}
193

194
BufferInterface *BufferInterface::get(Display *display, wl_resource *r)
195
196
197
198
199
200
201
202
203
{
    if (!r) {
        return nullptr;
    }
    // TODO: verify it's a buffer
    BufferInterface *b = Private::get(r);
    if (b) {
        return b;
    }
204
    return new BufferInterface(display, r);
205
206
}

207
BufferInterface::BufferInterface(Display *display, wl_resource *resource)
208
    : QObject()
209
    , d(new Private(this, display, resource))
210
211
212
213
214
{
}

BufferInterface::~BufferInterface()
{
215
216
217
    if (d->refCount != 0) {
        qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount;
    }
218
219
}

220
221
222
223
224
225
void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data)
{
    Q_UNUSED(listener);
    auto b = cast(reinterpret_cast<wl_resource*>(data));
    b->buffer = nullptr;
    emit b->q->aboutToBeDestroyed(b->q);
226
    delete b->q;
227
228
}

229
230
void BufferInterface::ref()
{
231
    d->refCount++;
232
233
234
235
}

void BufferInterface::unref()
{
236
237
238
    Q_ASSERT(d->refCount > 0);
    d->refCount--;
    if (d->refCount == 0) {
239
240
        if (d->buffer) {
            wl_buffer_send_release(d->buffer);
241
            wl_client_flush(wl_resource_get_client(d->buffer));
242
        }
243
244
245
    }
}

246
QImage::Format BufferInterface::Private::format() const
247
{
248
249
250
    if (!shmBuffer) {
        return QImage::Format_Invalid;
    }
251
    switch (wl_shm_buffer_get_format(shmBuffer)) {
252
    case WL_SHM_FORMAT_ARGB8888:
253
        return QImage::Format_ARGB32_Premultiplied;
254
255
256
257
258
259
260
261
262
    case WL_SHM_FORMAT_XRGB8888:
        return QImage::Format_RGB32;
    default:
        return QImage::Format_Invalid;
    }
}

QImage BufferInterface::data()
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
263
    return d->createImage();
264
265
}

266
QImage BufferInterface::Private::createImage()
267
{
268
    if (!shmBuffer) {
269
270
271
272
        return QImage();
    }
    if (s_accessedBuffer != nullptr && s_accessedBuffer != this) {
        return QImage();
273
274
275
    }
    const QImage::Format imageFormat = format();
    if (imageFormat == QImage::Format_Invalid) {
276
        return QImage();
277
    }
278
279
    s_accessedBuffer = this;
    s_accessCounter++;
280
    wl_shm_buffer_begin_access(shmBuffer);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
281
282
283
284
285
286
    return QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer),
                  size.width(),
                  size.height(),
                  wl_shm_buffer_get_stride(shmBuffer),
                  imageFormat,
                  &imageBufferCleanupHandler, this);
287
288
289
290
291
292
293
294
295
296
}

bool BufferInterface::isReferenced() const
{
    return d->refCount > 0;
}

wl_shm_buffer *BufferInterface::shmBuffer()
{
    return d->shmBuffer;
297
298
}

Roman Gilg's avatar
Roman Gilg committed
299
300
301
302
303
LinuxDmabufBuffer *BufferInterface::linuxDmabufBuffer()
{
    return d->dmabufBuffer;
}

304
305
306
307
308
wl_resource *BufferInterface::resource() const
{
    return d->buffer;
}

309
310
311
312
313
314
315
316
317
318
int BufferInterface::width() const
{
    return d->size.width();
}

int BufferInterface::height() const
{
    return d->size.height();
}

319
320
321
322
323
324
325
326
327
328
329
330
331
332
QSize BufferInterface::size() const
{
    return d->size;
}

void BufferInterface::setSize(const QSize &size)
{
    if (d->shmBuffer || d->size == size) {
        return;
    }
    d->size = size;
    emit sizeChanged();
}

333
334
335
336
337
bool BufferInterface::hasAlphaChannel() const
{
    return d->alpha;
}

338
}