master
c 284 lines 7.78 KB
Raw
1 #include "qemu/osdep.h"
2 #include "qemu/error-report.h"
3 #include "qemu/module.h"
4 #include "qapi/error.h"
5 #include "ui/console.h"
6 #include "ui/egl-helpers.h"
7 #include "ui/egl-context.h"
8 #include "ui/shader.h"
9
10 typedef struct egl_dpy {
11 DisplayChangeListener dcl;
12 DisplayGLCtx *ctx;
13 DisplaySurface *ds;
14 QemuGLShader *gls;
15 egl_fb guest_fb;
16 egl_fb cursor_fb;
17 egl_fb blit_fb;
18 bool y_0_top;
19 uint32_t pos_x;
20 uint32_t pos_y;
21 } egl_dpy;
22
23 static GPtrArray *egl_dpys;
24
25 /* ------------------------------------------------------------------ */
26
27 static void egl_refresh(DisplayChangeListener *dcl)
28 {
29 qemu_console_hw_update(dcl->con);
30 }
31
32 static void egl_gfx_update(DisplayChangeListener *dcl,
33 int x, int y, int w, int h)
34 {
35 }
36
37 static void egl_gfx_switch(DisplayChangeListener *dcl,
38 struct DisplaySurface *new_surface)
39 {
40 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
41
42 edpy->ds = new_surface;
43 }
44
45 static QEMUGLContext egl_create_context(DisplayGLCtx *dgc,
46 QEMUGLParams *params)
47 {
48 return qemu_egl_create_context(dgc, params, qemu_egl_rn_ctx);
49 }
50
51 static void egl_scanout_disable(DisplayChangeListener *dcl)
52 {
53 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
54
55 egl_fb_destroy(&edpy->guest_fb);
56 egl_fb_destroy(&edpy->blit_fb);
57 }
58
59 static void egl_scanout_texture(DisplayChangeListener *dcl,
60 uint32_t backing_id,
61 bool backing_y_0_top,
62 uint32_t backing_width,
63 uint32_t backing_height,
64 uint32_t x, uint32_t y,
65 uint32_t w, uint32_t h,
66 void *d3d_tex2d)
67 {
68 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
69
70 edpy->y_0_top = backing_y_0_top;
71
72 /* source framebuffer */
73 egl_fb_setup_for_tex(&edpy->guest_fb,
74 backing_width, backing_height, backing_id, false);
75
76 /* dest framebuffer */
77 if (edpy->blit_fb.width != backing_width ||
78 edpy->blit_fb.height != backing_height) {
79 egl_fb_destroy(&edpy->blit_fb);
80 egl_fb_setup_new_tex(&edpy->blit_fb, backing_width, backing_height);
81 }
82 }
83
84 #ifdef CONFIG_GBM
85
86 static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
87 QemuDmaBuf *dmabuf)
88 {
89 uint32_t width, height, texture;
90
91 egl_dmabuf_import_texture(dmabuf);
92 texture = qemu_dmabuf_get_texture(dmabuf);
93 if (!texture) {
94 return;
95 }
96
97 width = qemu_dmabuf_get_width(dmabuf);
98 height = qemu_dmabuf_get_height(dmabuf);
99
100 egl_scanout_texture(dcl, texture, false, width, height, 0, 0,
101 width, height, NULL);
102 }
103
104 static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
105 QemuDmaBuf *dmabuf, bool have_hot,
106 uint32_t hot_x, uint32_t hot_y)
107 {
108 uint32_t width, height, texture;
109 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
110
111 if (dmabuf) {
112 egl_dmabuf_import_texture(dmabuf);
113 texture = qemu_dmabuf_get_texture(dmabuf);
114 if (!texture) {
115 return;
116 }
117
118 width = qemu_dmabuf_get_width(dmabuf);
119 height = qemu_dmabuf_get_height(dmabuf);
120 egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false);
121 } else {
122 egl_fb_destroy(&edpy->cursor_fb);
123 }
124 }
125
126 static void egl_release_dmabuf(DisplayChangeListener *dcl,
127 QemuDmaBuf *dmabuf)
128 {
129 egl_dmabuf_release_texture(dmabuf);
130 }
131
132 #endif
133
134 static void egl_cursor_position(DisplayChangeListener *dcl,
135 uint32_t pos_x, uint32_t pos_y)
136 {
137 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
138
139 edpy->pos_x = pos_x;
140 edpy->pos_y = pos_y;
141 }
142
143 static void egl_scanout_flush(DisplayChangeListener *dcl,
144 uint32_t x, uint32_t y,
145 uint32_t w, uint32_t h)
146 {
147 egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
148
149 if (!edpy->guest_fb.texture || !edpy->ds) {
150 return;
151 }
152 assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
153
154 if (edpy->cursor_fb.texture) {
155 /* have cursor -> render using textures */
156 egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
157 !edpy->y_0_top);
158 egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
159 !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
160 1.0, 1.0);
161 } else {
162 /* no cursor -> use simple framebuffer blit */
163 egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
164 }
165
166 egl_fb_read(edpy->ds, &edpy->blit_fb);
167 qemu_console_update(edpy->dcl.con, x, y, w, h);
168 }
169
170 static const DisplayChangeListenerOps egl_ops = {
171 .dpy_name = "egl-headless",
172 .dpy_refresh = egl_refresh,
173 .dpy_gfx_update = egl_gfx_update,
174 .dpy_gfx_switch = egl_gfx_switch,
175
176 .dpy_gl_scanout_disable = egl_scanout_disable,
177 .dpy_gl_scanout_texture = egl_scanout_texture,
178 #ifdef CONFIG_GBM
179 .dpy_gl_scanout_dmabuf = egl_scanout_dmabuf,
180 .dpy_gl_cursor_dmabuf = egl_cursor_dmabuf,
181 .dpy_gl_release_dmabuf = egl_release_dmabuf,
182 #endif
183 .dpy_gl_cursor_position = egl_cursor_position,
184 .dpy_gl_update = egl_scanout_flush,
185 };
186
187 static bool
188 egl_is_compatible_dcl(DisplayGLCtx *dgc,
189 DisplayChangeListener *dcl)
190 {
191 if (!dcl->ops->dpy_gl_update) {
192 /*
193 * egl-headless is compatible with all 2d listeners, as it blits the GL
194 * updates on the 2d console surface.
195 */
196 return true;
197 }
198
199 return dcl->ops == &egl_ops;
200 }
201
202 static const DisplayGLCtxOps eglctx_ops = {
203 .dpy_gl_ctx_is_compatible_dcl = egl_is_compatible_dcl,
204 .dpy_gl_ctx_create = egl_create_context,
205 .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
206 .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
207 };
208
209 static void early_egl_headless_init(DisplayOptions *opts)
210 {
211 DisplayGLMode mode = DISPLAY_GL_MODE_ON;
212
213 if (opts->has_gl) {
214 mode = opts->gl;
215 }
216
217 egl_init(opts->u.egl_headless.rendernode, mode, &error_fatal);
218 }
219
220 static void egl_headless_init(DisplayState *ds, DisplayOptions *opts)
221 {
222 QemuConsole *con;
223 egl_dpy *edpy;
224 int idx;
225
226 egl_dpys = g_ptr_array_new();
227
228 for (idx = 0;; idx++) {
229 DisplayGLCtx *ctx;
230
231 con = qemu_console_lookup_by_index(idx);
232 if (!con || !qemu_console_is_graphic(con)) {
233 break;
234 }
235
236 edpy = g_new0(egl_dpy, 1);
237 edpy->gls = qemu_gl_init_shader();
238 ctx = g_new0(DisplayGLCtx, 1);
239 ctx->ops = &eglctx_ops;
240 edpy->ctx = ctx;
241 qemu_console_set_display_gl_ctx(con, ctx);
242 qemu_console_register_listener(con, &edpy->dcl, &egl_ops);
243 g_ptr_array_add(egl_dpys, edpy);
244 }
245 }
246
247 static void egl_headless_cleanup(void)
248 {
249 if (!egl_dpys) {
250 return;
251 }
252
253 for (guint i = 0; i < egl_dpys->len; i++) {
254 egl_dpy *edpy = g_ptr_array_index(egl_dpys, i);
255
256 qemu_console_unregister_listener(&edpy->dcl);
257 qemu_console_set_display_gl_ctx(edpy->dcl.con, NULL);
258 egl_fb_destroy(&edpy->guest_fb);
259 egl_fb_destroy(&edpy->cursor_fb);
260 egl_fb_destroy(&edpy->blit_fb);
261 qemu_gl_fini_shader(edpy->gls);
262 g_free(edpy->ctx);
263 g_free(edpy);
264 }
265 g_clear_pointer(&egl_dpys, g_ptr_array_unref);
266
267 egl_cleanup();
268 }
269
270 static QemuDisplay qemu_display_egl = {
271 .type = DISPLAY_TYPE_EGL_HEADLESS,
272 .early_init = early_egl_headless_init,
273 .init = egl_headless_init,
274 .cleanup = egl_headless_cleanup,
275 };
276
277 static void register_egl(void)
278 {
279 qemu_display_register(&qemu_display_egl);
280 }
281
282 type_init(register_egl);
283
284 module_dep("ui-opengl");