Skip to content

Commit beab7b5

Browse files
committed
add offscreen (MESA_platform_surfaceless) rendering surface
1 parent c1dd764 commit beab7b5

File tree

6 files changed

+744
-15
lines changed

6 files changed

+744
-15
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ if (ENABLE_OPENGL)
207207
if (EGL_FOUND AND GLES2_FOUND)
208208
target_sources(flutterpi_module PRIVATE
209209
src/egl_gbm_render_surface.c
210+
src/egl_offscreen_render_surface.c
210211
src/gl_renderer.c
211212
)
212213
target_link_libraries(flutterpi_module PUBLIC

src/egl_offscreen_render_surface.c

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
// SPDX-License-Identifier: MIT
2+
/*
3+
* GBM Surface Backing Stores
4+
*
5+
* - implements EGL/GL render surfaces using a gbm surface
6+
* - ideal way to create render surfaces right now
7+
*
8+
* Copyright (c) 2022, Hannes Winkler <[email protected]>
9+
*/
10+
11+
#include "egl_offscreen_render_surface.h"
12+
13+
#include <errno.h>
14+
#include <stdlib.h>
15+
16+
#include "egl.h"
17+
#include "gl_renderer.h"
18+
#include "gles.h"
19+
#include "modesetting.h"
20+
#include "pixel_format.h"
21+
#include "render_surface.h"
22+
#include "render_surface_private.h"
23+
#include "surface.h"
24+
#include "tracer.h"
25+
#include "util/collection.h"
26+
#include "util/logging.h"
27+
#include "util/refcounting.h"
28+
29+
struct egl_offscreen_render_surface;
30+
31+
struct egl_offscreen_render_surface {
32+
union {
33+
struct surface surface;
34+
struct render_surface render_surface;
35+
};
36+
37+
#ifdef DEBUG
38+
uuid_t uuid;
39+
#endif
40+
41+
enum pixfmt pixel_format;
42+
EGLDisplay egl_display;
43+
EGLSurface egl_surface;
44+
EGLConfig egl_config;
45+
struct gl_renderer *renderer;
46+
};
47+
48+
COMPILE_ASSERT(offsetof(struct egl_offscreen_render_surface, surface) == 0);
49+
COMPILE_ASSERT(offsetof(struct egl_offscreen_render_surface, render_surface.surface) == 0);
50+
51+
#ifdef DEBUG
52+
static const uuid_t uuid = CONST_UUID(0xf9, 0xab, 0x5d, 0xad, 0x2e, 0x3b, 0x4e, 0x2c, 0x9d, 0x26, 0x64, 0x70, 0xfa, 0x9a, 0x25, 0xab);
53+
#endif
54+
55+
#define CAST_THIS(ptr) CAST_EGL_OFFSCREEN_RENDER_SURFACE(ptr)
56+
#define CAST_THIS_UNCHECKED(ptr) CAST_EGL_OFFSCREEN_RENDER_SURFACE_UNCHECKED(ptr)
57+
58+
#ifdef DEBUG
59+
ATTR_PURE struct egl_offscreen_render_surface *__checked_cast_egl_offscreen_render_surface(void *ptr) {
60+
struct egl_offscreen_render_surface *s;
61+
62+
s = CAST_EGL_OFFSCREEN_RENDER_SURFACE_UNCHECKED(ptr);
63+
assert(uuid_equals(s->uuid, uuid));
64+
return s;
65+
}
66+
#endif
67+
68+
void egl_offscreen_render_surface_deinit(struct surface *s);
69+
static int egl_offscreen_render_surface_present_kms(struct surface *s, const struct fl_layer_props *props, struct kms_req_builder *builder);
70+
static int egl_offscreen_render_surface_present_fbdev(struct surface *s, const struct fl_layer_props *props, struct fbdev_commit_builder *builder);
71+
static int egl_offscreen_render_surface_fill(struct render_surface *s, FlutterBackingStore *fl_store);
72+
static int egl_offscreen_render_surface_queue_present(struct render_surface *s, const FlutterBackingStore *fl_store);
73+
74+
static int egl_offscreen_render_surface_init(
75+
struct egl_offscreen_render_surface *s,
76+
struct tracer *tracer,
77+
struct vec2i size,
78+
struct gl_renderer *renderer,
79+
enum pixfmt pixel_format,
80+
EGLConfig egl_config
81+
) {
82+
EGLDisplay egl_display;
83+
EGLSurface egl_surface;
84+
EGLBoolean egl_ok;
85+
int ok;
86+
87+
ASSERT_NOT_NULL(renderer);
88+
ASSUME_PIXFMT_VALID(pixel_format);
89+
egl_display = gl_renderer_get_egl_display(renderer);
90+
ASSERT_NOT_NULL(egl_display);
91+
92+
#ifdef DEBUG
93+
if (egl_config != EGL_NO_CONFIG_KHR) {
94+
EGLint value = 0;
95+
96+
egl_ok = eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &value);
97+
if (egl_ok == EGL_FALSE) {
98+
LOG_EGL_ERROR(eglGetError(), "Couldn't query pixel format of EGL framebuffer config. eglGetConfigAttrib");
99+
return EIO;
100+
}
101+
102+
ASSERT_EQUALS_MSG(
103+
value,
104+
get_pixfmt_info(pixel_format)->gbm_format,
105+
"EGL framebuffer config pixel format doesn't match the argument pixel format."
106+
);
107+
}
108+
#endif
109+
110+
/// TODO: Think about allowing different tilings / modifiers here
111+
if (egl_config == EGL_NO_CONFIG_KHR) {
112+
// choose a config
113+
egl_config = gl_renderer_choose_config_direct(renderer, pixel_format);
114+
if (egl_config == EGL_NO_CONFIG_KHR) {
115+
LOG_ERROR(
116+
"EGL doesn't supported the specified pixel format %s. Try a different one (ARGB8888 should always work).\n",
117+
get_pixfmt_info(pixel_format)->name
118+
);
119+
return EINVAL;
120+
}
121+
}
122+
123+
// EGLAttribKHR is defined by EGL_KHR_cl_event2.
124+
#ifndef EGL_KHR_cl_event2
125+
#error "EGL header definitions for extension EGL_KHR_cl_event2 are required."
126+
#endif
127+
128+
static const EGLAttribKHR surface_attribs[] = {
129+
/* EGL_GL_COLORSPACE, GL_LINEAR / GL_SRGB */
130+
/* EGL_RENDER_BUFFER, EGL_BACK_BUFFER / EGL_SINGLE_BUFFER */
131+
/* EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_NONPRE / EGL_VG_ALPHA_FORMAT_PRE */
132+
/* EGL_VG_COLORSPACE, EGL_VG_COLORSPACE_sRGB / EGL_VG_COLORSPACE_LINEAR */
133+
EGL_NONE,
134+
};
135+
136+
(void) surface_attribs;
137+
138+
egl_ok = eglBindAPI(EGL_OPENGL_ES_API);
139+
if (egl_ok == EGL_FALSE) {
140+
LOG_EGL_ERROR(eglGetError(), "Couldn't bind OpenGL ES API to EGL. eglBindAPI");
141+
return EIO;
142+
}
143+
144+
egl_surface = gl_renderer_create_pbuffer_surface(renderer, egl_config, NULL, NULL);
145+
if (egl_surface == EGL_NO_SURFACE) {
146+
return EIO;
147+
}
148+
149+
/// TODO: Implement
150+
ok = render_surface_init(CAST_RENDER_SURFACE_UNCHECKED(s), tracer, size);
151+
if (ok != 0) {
152+
goto fail_destroy_egl_surface;
153+
}
154+
155+
s->surface.present_kms = egl_offscreen_render_surface_present_kms;
156+
s->surface.present_fbdev = egl_offscreen_render_surface_present_fbdev;
157+
s->surface.deinit = egl_offscreen_render_surface_deinit;
158+
s->render_surface.fill = egl_offscreen_render_surface_fill;
159+
s->render_surface.queue_present = egl_offscreen_render_surface_queue_present;
160+
#ifdef DEBUG
161+
uuid_copy(&s->uuid, uuid);
162+
#endif
163+
s->pixel_format = pixel_format;
164+
s->egl_display = egl_display;
165+
s->egl_surface = egl_surface;
166+
s->egl_config = egl_config;
167+
s->renderer = gl_renderer_ref(renderer);
168+
return 0;
169+
170+
fail_destroy_egl_surface:
171+
eglDestroySurface(egl_display, egl_surface);
172+
173+
return ok;
174+
}
175+
176+
/**
177+
* @brief Create a new gbm_surface based render surface, with an explicit EGL Config for the created EGLSurface.
178+
*
179+
* @param compositor The compositor that this surface will be registered to when calling surface_register.
180+
* @param size The size of the surface.
181+
* @param renderer The EGL/OpenGL used to create any GL surfaces.
182+
* @param pixel_format The pixel format to be used by the framebuffers of the surface.
183+
* @param egl_config The EGLConfig used for creating the EGLSurface.
184+
* @param allowed_modifiers The list of modifiers that gbm_surface_create_with_modifiers can choose from.
185+
* NULL if not specified. (In that case, gbm_surface_create will be used)
186+
* @param n_allowed_modifiers The number of modifiers in @param allowed_modifiers.
187+
* @return struct egl_offscreen_render_surface*
188+
*/
189+
struct egl_offscreen_render_surface *egl_offscreen_render_surface_new_with_egl_config(
190+
struct tracer *tracer,
191+
struct vec2i size,
192+
struct gl_renderer *renderer,
193+
enum pixfmt pixel_format,
194+
EGLConfig egl_config
195+
) {
196+
struct egl_offscreen_render_surface *surface;
197+
int ok;
198+
199+
surface = malloc(sizeof *surface);
200+
if (surface == NULL) {
201+
goto fail_return_null;
202+
}
203+
204+
ok = egl_offscreen_render_surface_init(
205+
surface,
206+
tracer,
207+
size,
208+
renderer,
209+
pixel_format,
210+
egl_config
211+
);
212+
if (ok != 0) {
213+
goto fail_free_surface;
214+
}
215+
216+
return surface;
217+
218+
fail_free_surface:
219+
free(surface);
220+
221+
fail_return_null:
222+
return NULL;
223+
}
224+
225+
/**
226+
* @brief Create a new gbm_surface based render surface.
227+
*
228+
* @param compositor The compositor that this surface will be registered to when calling surface_register.
229+
* @param size The size of the surface.
230+
* @param device The GBM device used to allocate the surface.
231+
* @param renderer The EGL/OpenGL used to create any GL surfaces.
232+
* @param pixel_format The pixel format to be used by the framebuffers of the surface.
233+
* @return struct egl_offscreen_render_surface*
234+
*/
235+
struct egl_offscreen_render_surface *egl_offscreen_render_surface_new(
236+
struct tracer *tracer,
237+
struct vec2i size,
238+
struct gl_renderer *renderer,
239+
enum pixfmt pixel_format
240+
) {
241+
return egl_offscreen_render_surface_new_with_egl_config(tracer, size, renderer, pixel_format, EGL_NO_CONFIG_KHR);
242+
}
243+
244+
void egl_offscreen_render_surface_deinit(struct surface *s) {
245+
struct egl_offscreen_render_surface *egl_surface;
246+
247+
egl_surface = CAST_THIS(s);
248+
249+
gl_renderer_unref(egl_surface->renderer);
250+
render_surface_deinit(s);
251+
}
252+
253+
static int egl_offscreen_render_surface_present_kms(struct surface *s, const struct fl_layer_props *props, struct kms_req_builder *builder) {
254+
(void) s;
255+
(void) props;
256+
(void) builder;
257+
258+
UNIMPLEMENTED();
259+
260+
return 0;
261+
}
262+
263+
static int
264+
egl_offscreen_render_surface_present_fbdev(struct surface *s, const struct fl_layer_props *props, struct fbdev_commit_builder *builder) {
265+
struct egl_offscreen_render_surface *egl_surface;
266+
267+
/// TODO: Implement by mmapping the current front bo, copy it into the fbdev
268+
/// TODO: Print a warning here if we're not using explicit linear tiling and use glReadPixels instead of gbm_bo_map in that case
269+
270+
egl_surface = CAST_THIS(s);
271+
(void) egl_surface;
272+
(void) props;
273+
(void) builder;
274+
275+
UNIMPLEMENTED();
276+
277+
return 0;
278+
}
279+
280+
static int egl_offscreen_render_surface_fill(struct render_surface *s, FlutterBackingStore *fl_store) {
281+
fl_store->type = kFlutterBackingStoreTypeOpenGL;
282+
fl_store->open_gl = (FlutterOpenGLBackingStore
283+
){ .type = kFlutterOpenGLTargetTypeFramebuffer,
284+
.framebuffer = { /* for some reason flutter wants this to be GL_BGRA8_EXT, contrary to what the docs say */
285+
.target = GL_BGRA8_EXT,
286+
287+
/* 0 refers to the window surface, instead of to an FBO */
288+
.name = 0,
289+
290+
/*
291+
* even though the compositor will call surface_ref too to fill the FlutterBackingStore.user_data,
292+
* we need to ref two times because flutter will call both this destruction callback and the
293+
* compositor collect callback
294+
*/
295+
.user_data = surface_ref(CAST_SURFACE_UNCHECKED(s)),
296+
.destruction_callback = surface_unref_void } };
297+
return 0;
298+
}
299+
300+
static int egl_offscreen_render_surface_queue_present(struct render_surface *s, const FlutterBackingStore *fl_store) {
301+
(void) s;
302+
(void) fl_store;
303+
304+
// nothing to do here
305+
306+
return 0;
307+
}
308+
309+
/**
310+
* @brief Get the EGL Surface for rendering into this render surface.
311+
*
312+
* Flutter doesn't really support backing stores to be EGL Surfaces, so we have to hack around this, kinda.
313+
*
314+
* @param s
315+
* @return EGLSurface The EGLSurface associated with this render surface. Only valid for the lifetime of this egl_offscreen_render_surface.
316+
*/
317+
ATTR_PURE EGLSurface egl_offscreen_render_surface_get_egl_surface(struct egl_offscreen_render_surface *s) {
318+
return s->egl_surface;
319+
}
320+
321+
/**
322+
* @brief Get the EGLConfig that was used to create the EGLSurface for this render surface.
323+
*
324+
* If the display doesn't support EGL_KHR_no_config_context, we need to create the EGL rendering context with
325+
* the same EGLConfig as every EGLSurface we want to bind to it. So we can just let egl_offscreen_render_surface choose a config
326+
* and let flutter-pi query that config when creating the rendering contexts in that case.
327+
*
328+
* @param s
329+
* @return EGLConfig The chosen EGLConfig. Valid forever.
330+
*/
331+
ATTR_PURE EGLConfig egl_offscreen_render_surface_get_egl_config(struct egl_offscreen_render_surface *s) {
332+
return s->egl_config;
333+
}

src/egl_offscreen_render_surface.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: MIT
2+
/*
3+
* Offscreen (MESA surfaceless) backing store
4+
*
5+
* Copyright (c) 2022, Hannes Winkler <[email protected]>
6+
*/
7+
8+
#ifndef _FLUTTERPI_SRC_EGL_OFFSCREEN_RENDER_SURFACE_H
9+
#define _FLUTTERPI_SRC_EGL_OFFSCREEN_RENDER_SURFACE_H
10+
11+
#include "compositor_ng.h"
12+
#include "pixel_format.h"
13+
#include "util/collection.h"
14+
15+
struct render_surface;
16+
struct gbm_device;
17+
struct egl_offscreen_render_surface;
18+
19+
#define CAST_EGL_OFFSCREEN_RENDER_SURFACE_UNCHECKED(ptr) ((struct egl_offscreen_render_surface *) (ptr))
20+
#ifdef DEBUG
21+
#define CAST_EGL_OFFSCREEN_RENDER_SURFACE(ptr) __checked_cast_egl_offscreen_render_surface(ptr)
22+
ATTR_PURE struct egl_offscreen_render_surface *__checked_cast_egl_offscreen_render_surface(void *ptr);
23+
#else
24+
#define CAST_EGL_OFFSCREEN_RENDER_SURFACE(ptr) CAST_EGL_OFFSCREEN_RENDER_SURFACE_UNCHECKED(ptr)
25+
#endif
26+
27+
struct egl_offscreen_render_surface *egl_offscreen_render_surface_new_with_egl_config(
28+
struct tracer *tracer,
29+
struct vec2i size,
30+
struct gl_renderer *renderer,
31+
enum pixfmt pixel_format,
32+
EGLConfig egl_config
33+
);
34+
35+
struct egl_offscreen_render_surface *egl_offscreen_render_surface_new(
36+
struct tracer *tracer,
37+
struct vec2i size,
38+
struct gl_renderer *renderer,
39+
enum pixfmt pixel_format
40+
);
41+
42+
ATTR_PURE EGLSurface egl_offscreen_render_surface_get_egl_surface(struct egl_offscreen_render_surface *s);
43+
44+
ATTR_PURE EGLConfig egl_offscreen_render_surface_get_egl_config(struct egl_offscreen_render_surface *s);
45+
46+
#endif // _FLUTTERPI_SRC_EGL_GBM_RENDER_SURFACE_H

0 commit comments

Comments
 (0)