Skip to content

Commit ea814c4

Browse files
committed
Add WOutputLayer
Support use wlr_output_layer to a QQuickItem. Closes: #189
1 parent 383448c commit ea814c4

22 files changed

+2308
-487
lines changed

examples/tinywl/Main.qml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ Item {
1919
onOutputAdded: function(output) {
2020
output.forceSoftwareCursor = true // Test
2121

22-
if (QmlHelper.outputManager.count > 0)
23-
output.scale = 2
24-
2522
Helper.allowNonDrmOutputAutoChangeMode(output)
2623
QmlHelper.outputManager.add({waylandOutput: output})
2724
}

examples/tinywl/OutputDelegate.qml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ OutputItem {
1717
required property OutputCursor cursor
1818

1919
visible: cursor.visible && !cursor.isHardwareCursor
20+
width: cursor.size.width
21+
height: cursor.size.height
22+
OutputLayer.enabled: true
23+
OutputLayer.outputs: [onscreenViewport]
2024

2125
Image {
2226
id: cursorImage
@@ -102,6 +106,7 @@ OutputItem {
102106
root: true
103107
output: waylandOutput
104108
devicePixelRatio: outputViewport.devicePixelRatio
109+
layerFlags: OutputViewport.AlwaysAccepted
105110

106111
TextureProxy {
107112
sourceItem: outputViewport

src/server/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ set(QTQUICK_SOURCES
8484
qtquick/weventjunkman.cpp
8585
qtquick/wrenderhelper.cpp
8686
qtquick/wquicktextureproxy.cpp
87+
qtquick/woutputlayer.cpp
8788

8889
qtquick/private/wquickxdgshell.cpp
8990
qtquick/private/wquickbackend.cpp
@@ -103,6 +104,8 @@ set(QTQUICK_SOURCES
103104
qtquick/private/wquickvirtualkeyboardv1.cpp
104105
qtquick/private/winputmethodhelper.cpp
105106
qtquick/private/wquickforeigntoplevelmanagerv1.cpp
107+
qtquick/private/wbufferrenderer.cpp
108+
106109
${CMAKE_CURRENT_BINARY_DIR}/text-input-unstable-v1-protocol.c
107110
)
108111

@@ -167,6 +170,7 @@ set(HEADERS
167170
qtquick/weventjunkman.h
168171
qtquick/wrenderhelper.h
169172
qtquick/wquicktextureproxy.h
173+
qtquick/woutputlayer.h
170174

171175
utils/wtools.h
172176
utils/wthreadutils.h
@@ -194,6 +198,8 @@ set(PRIVATE_HEADERS
194198
qtquick/private/wquickvirtualkeyboardv1_p.h
195199
qtquick/private/winputmethodhelper_p.h
196200
qtquick/private/wquickforeigntoplevelmanagerv1_p.h
201+
qtquick/private/wbufferrenderer_p.h
202+
197203
${CMAKE_CURRENT_BINARY_DIR}/text-input-unstable-v1-protocol.h
198204
)
199205

src/server/kernel/woutput.cpp

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,22 @@ extern "C" {
2121
#include <wlr/render/interface.h>
2222
#undef static
2323
#include <wlr/types/wlr_output_layout.h>
24+
#ifdef slots
25+
#undef slots // using at swapchain.h
26+
#endif
27+
#include <wlr/render/swapchain.h>
28+
#include <wlr/render/allocator.h>
29+
#include <wlr/render/wlr_renderer.h>
2430
}
2531

2632
#include <QLoggingCategory>
2733
#include <QCoreApplication>
2834
#include <QQuickWindow>
2935
#include <QCursor>
3036

37+
#include <xf86drm.h>
38+
#include <drm_fourcc.h>
39+
3140
QW_USE_NAMESPACE
3241
WAYLIB_SERVER_BEGIN_NAMESPACE
3342

@@ -139,6 +148,262 @@ QWAllocator *WOutput::allocator() const
139148
return QWAllocator::from(d->nativeHandle()->allocator);
140149
}
141150

151+
// Copy from wlroots
152+
static const struct wlr_drm_format_set *wlr_renderer_get_render_formats(
153+
struct wlr_renderer *r) {
154+
if (!r->impl->get_render_formats) {
155+
return NULL;
156+
}
157+
return r->impl->get_render_formats(r);
158+
}
159+
160+
static bool wlr_drm_format_copy(struct wlr_drm_format *dst, const struct wlr_drm_format *src) {
161+
assert(src->len <= src->capacity);
162+
163+
uint64_t *modifiers = reinterpret_cast<uint64_t*>(malloc(sizeof(*modifiers) * src->len));
164+
if (!modifiers) {
165+
return false;
166+
}
167+
168+
memcpy(modifiers, src->modifiers, sizeof(*modifiers) * src->len);
169+
170+
wlr_drm_format_finish(dst);
171+
dst->capacity = src->len;
172+
dst->len = src->len;
173+
dst->format = src->format;
174+
dst->modifiers = modifiers;
175+
return true;
176+
}
177+
178+
static bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier) {
179+
for (size_t i = 0; i < fmt->len; ++i) {
180+
if (fmt->modifiers[i] == modifier) {
181+
return true;
182+
}
183+
}
184+
return false;
185+
}
186+
187+
static bool wlr_drm_format_add(struct wlr_drm_format *fmt, uint64_t modifier) {
188+
if (wlr_drm_format_has(fmt, modifier)) {
189+
return true;
190+
}
191+
192+
if (fmt->len == fmt->capacity) {
193+
size_t capacity = fmt->capacity ? fmt->capacity * 2 : 4;
194+
195+
uint64_t *new_modifiers = reinterpret_cast<uint64_t*>(realloc(fmt->modifiers, sizeof(*fmt->modifiers) * capacity));
196+
if (!new_modifiers) {
197+
qCritical("Allocation failed");
198+
return false;
199+
}
200+
201+
fmt->capacity = capacity;
202+
fmt->modifiers = new_modifiers;
203+
}
204+
205+
fmt->modifiers[fmt->len++] = modifier;
206+
return true;
207+
}
208+
209+
static bool wlr_drm_format_intersect(struct wlr_drm_format *dst,
210+
const struct wlr_drm_format *a, const struct wlr_drm_format *b) {
211+
assert(a->format == b->format);
212+
213+
size_t capacity = a->len < b->len ? a->len : b->len;
214+
uint64_t *modifiers = reinterpret_cast<uint64_t*>(malloc(sizeof(*modifiers) * capacity));
215+
if (!modifiers) {
216+
return false;
217+
}
218+
219+
wlr_drm_format fmt = {
220+
.format = a->format,
221+
.len = 0,
222+
.capacity = capacity,
223+
.modifiers = modifiers,
224+
};
225+
226+
for (size_t i = 0; i < a->len; i++) {
227+
for (size_t j = 0; j < b->len; j++) {
228+
if (a->modifiers[i] == b->modifiers[j]) {
229+
assert(fmt.len < fmt.capacity);
230+
fmt.modifiers[fmt.len++] = a->modifiers[i];
231+
break;
232+
}
233+
}
234+
}
235+
236+
wlr_drm_format_finish(dst);
237+
*dst = fmt;
238+
return true;
239+
}
240+
241+
static bool output_pick_format(struct wlr_output *output,
242+
const struct wlr_drm_format_set *display_formats,
243+
struct wlr_drm_format *format, uint32_t fmt) {
244+
struct wlr_renderer *renderer = output->renderer;
245+
struct wlr_allocator *allocator = output->allocator;
246+
assert(renderer != NULL && allocator != NULL);
247+
248+
const struct wlr_drm_format_set *render_formats =
249+
wlr_renderer_get_render_formats(renderer);
250+
if (render_formats == NULL) {
251+
qCritical("Failed to get render formats");
252+
return false;
253+
}
254+
255+
const struct wlr_drm_format *render_format =
256+
wlr_drm_format_set_get(render_formats, fmt);
257+
if (render_format == NULL) {
258+
qDebug("Renderer doesn't support format 0x%" PRIX32, fmt);
259+
return false;
260+
}
261+
262+
if (display_formats != NULL) {
263+
const struct wlr_drm_format *display_format =
264+
wlr_drm_format_set_get(display_formats, fmt);
265+
if (display_format == NULL) {
266+
qDebug("Output doesn't support format 0x%" PRIX32, fmt);
267+
return false;
268+
}
269+
if (!wlr_drm_format_intersect(format, display_format, render_format)) {
270+
qDebug("Failed to intersect display and render "
271+
"modifiers for format 0x%" PRIX32 " on output %s",
272+
fmt, output->name);
273+
return false;
274+
}
275+
} else {
276+
// The output can display any format
277+
if (!wlr_drm_format_copy(format, render_format)) {
278+
return false;
279+
}
280+
}
281+
282+
if (format->len == 0) {
283+
wlr_drm_format_finish(format);
284+
qDebug("Failed to pick output format");
285+
return false;
286+
}
287+
288+
return true;
289+
}
290+
291+
static struct wlr_swapchain *create_swapchain(struct wlr_output *output,
292+
int width, int height,
293+
uint32_t render_format,
294+
bool allow_modifiers) {
295+
wlr_allocator *allocator = output->allocator;
296+
assert(output->allocator != NULL);
297+
298+
const struct wlr_drm_format_set *display_formats =
299+
wlr_output_get_primary_formats(output, allocator->buffer_caps);
300+
struct wlr_drm_format format = {0};
301+
if (!output_pick_format(output, display_formats, &format, render_format)) {
302+
qDebug("Failed to pick primary buffer format for output '%s'",
303+
output->name);
304+
return NULL;
305+
}
306+
307+
char *format_name = drmGetFormatName(format.format);
308+
qDebug("Choosing primary buffer format %s (0x%08" PRIX32 ") for output '%s'",
309+
format_name ? format_name : "<unknown>", format.format, output->name);
310+
free(format_name);
311+
312+
if (!allow_modifiers && (format.len != 1 || format.modifiers[0] != DRM_FORMAT_MOD_LINEAR)) {
313+
if (!wlr_drm_format_has(&format, DRM_FORMAT_MOD_INVALID)) {
314+
qDebug("Implicit modifiers not supported");
315+
wlr_drm_format_finish(&format);
316+
return NULL;
317+
}
318+
319+
format.len = 0;
320+
if (!wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID)) {
321+
qDebug("Failed to add implicit modifier to format");
322+
wlr_drm_format_finish(&format);
323+
return NULL;
324+
}
325+
}
326+
327+
struct wlr_swapchain *swapchain = wlr_swapchain_create(allocator, width, height, &format);
328+
wlr_drm_format_finish(&format);
329+
return swapchain;
330+
}
331+
332+
static bool test_swapchain(struct wlr_output *output,
333+
struct wlr_swapchain *swapchain, const struct wlr_output_state *state) {
334+
struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL);
335+
if (buffer == NULL) {
336+
return false;
337+
}
338+
339+
struct wlr_output_state copy = *state;
340+
copy.committed |= WLR_OUTPUT_STATE_BUFFER;
341+
copy.buffer = buffer;
342+
bool ok = wlr_output_test_state(output, &copy);
343+
wlr_buffer_unlock(buffer);
344+
return ok;
345+
}
346+
347+
static bool wlr_output_configure_primary_swapchain(struct wlr_output *output, int width, int height,
348+
uint32_t format, struct wlr_swapchain **swapchain_ptr,
349+
bool test) {
350+
wlr_output_state empty_state;
351+
wlr_output_state_init(&empty_state);
352+
wlr_output_state *state = &empty_state;
353+
354+
// Re-use the existing swapchain if possible
355+
struct wlr_swapchain *old_swapchain = *swapchain_ptr;
356+
if (old_swapchain != NULL &&
357+
old_swapchain->width == width && old_swapchain->height == height &&
358+
old_swapchain->format.format == format) {
359+
return true;
360+
}
361+
362+
struct wlr_swapchain *swapchain = create_swapchain(output, width, height, format, true);
363+
if (swapchain == NULL) {
364+
qCritical("Failed to create swapchain for output '%s'", output->name);
365+
return false;
366+
}
367+
368+
if (test) {
369+
qDebug("Testing swapchain for output '%s'", output->name);
370+
if (!test_swapchain(output, swapchain, state)) {
371+
qDebug("Output test failed on '%s', retrying without modifiers",
372+
output->name);
373+
wlr_swapchain_destroy(swapchain);
374+
swapchain = create_swapchain(output, width, height, format, false);
375+
if (swapchain == NULL) {
376+
qCritical("Failed to create modifier-less swapchain for output '%s'",
377+
output->name);
378+
return false;
379+
}
380+
qDebug("Testing modifier-less swapchain for output '%s'", output->name);
381+
if (!test_swapchain(output, swapchain, state)) {
382+
qCritical("Swapchain for output '%s' failed test", output->name);
383+
wlr_swapchain_destroy(swapchain);
384+
return false;
385+
}
386+
}
387+
}
388+
389+
wlr_swapchain_destroy(*swapchain_ptr);
390+
*swapchain_ptr = swapchain;
391+
return true;
392+
}
393+
// End
394+
395+
bool WOutput::configureSwapchain(const QSize &size, uint32_t format,
396+
QWSwapchain **swapchain, bool doTest)
397+
{
398+
wlr_swapchain *sc = (*swapchain)->handle();
399+
bool ok = wlr_output_configure_primary_swapchain(nativeHandle(), size.width(), size.height(),
400+
format, &sc, doTest);
401+
if (!ok)
402+
return false;
403+
*swapchain = QWSwapchain::from(sc);
404+
return true;
405+
}
406+
142407
QWOutput *WOutput::handle() const
143408
{
144409
W_DC(WOutput);

src/server/kernel/woutput.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class WAYLIB_SERVER_EXPORT WOutput : public QObject, public WObject
6969
QW_NAMESPACE::QWRenderer *renderer() const;
7070
QW_NAMESPACE::QWSwapchain *swapchain() const;
7171
QW_NAMESPACE::QWAllocator *allocator() const;
72+
bool configureSwapchain(const QSize &size, uint32_t format,
73+
QW_NAMESPACE::QWSwapchain **swapchain,
74+
bool doTest = true);
7275

7376
QW_NAMESPACE::QWOutput *handle() const;
7477
wlr_output *nativeHandle() const;

0 commit comments

Comments
 (0)