xref: /openbmc/qemu/ui/egl-headless.c (revision a9ded601)
1 #include "qemu/osdep.h"
2 #include "qemu-common.h"
3 #include "sysemu/sysemu.h"
4 #include "ui/console.h"
5 #include "ui/egl-helpers.h"
6 #include "ui/egl-context.h"
7 
8 typedef struct egl_dpy {
9     DisplayChangeListener dcl;
10     DisplaySurface *ds;
11     int width, height;
12     GLuint texture;
13     GLuint framebuffer;
14     GLuint blit_texture;
15     GLuint blit_framebuffer;
16     bool y_0_top;
17 } egl_dpy;
18 
19 static void egl_refresh(DisplayChangeListener *dcl)
20 {
21     graphic_hw_update(dcl->con);
22 }
23 
24 static void egl_gfx_update(DisplayChangeListener *dcl,
25                            int x, int y, int w, int h)
26 {
27 }
28 
29 static void egl_gfx_switch(DisplayChangeListener *dcl,
30                            struct DisplaySurface *new_surface)
31 {
32     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
33 
34     edpy->ds = new_surface;
35 }
36 
37 static void egl_scanout_disable(DisplayChangeListener *dcl)
38 {
39     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
40 
41     edpy->texture = 0;
42     /* XXX: delete framebuffers here ??? */
43 }
44 
45 static void egl_scanout_texture(DisplayChangeListener *dcl,
46                                 uint32_t backing_id,
47                                 bool backing_y_0_top,
48                                 uint32_t backing_width,
49                                 uint32_t backing_height,
50                                 uint32_t x, uint32_t y,
51                                 uint32_t w, uint32_t h)
52 {
53     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
54 
55     edpy->texture = backing_id;
56     edpy->y_0_top = backing_y_0_top;
57 
58     /* source framebuffer */
59     if (!edpy->framebuffer) {
60         glGenFramebuffers(1, &edpy->framebuffer);
61     }
62     glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer);
63     glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
64                               GL_TEXTURE_2D, edpy->texture, 0);
65 
66     /* dest framebuffer */
67     if (!edpy->blit_framebuffer) {
68         glGenFramebuffers(1, &edpy->blit_framebuffer);
69         glGenTextures(1, &edpy->blit_texture);
70         edpy->width = 0;
71         edpy->height = 0;
72     }
73     if (edpy->width != backing_width || edpy->height != backing_height) {
74         edpy->width   = backing_width;
75         edpy->height  = backing_height;
76         glBindTexture(GL_TEXTURE_2D, edpy->blit_texture);
77         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
78                      edpy->width, edpy->height,
79                      0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
80         glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer);
81         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
82                                   GL_TEXTURE_2D, edpy->blit_texture, 0);
83     }
84 }
85 
86 static void egl_scanout_flush(DisplayChangeListener *dcl,
87                               uint32_t x, uint32_t y,
88                               uint32_t w, uint32_t h)
89 {
90     egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
91     GLuint y1, y2;
92 
93     if (!edpy->texture || !edpy->ds) {
94         return;
95     }
96     assert(surface_width(edpy->ds)  == edpy->width);
97     assert(surface_height(edpy->ds) == edpy->height);
98     assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
99 
100     /* blit framebuffer, flip if needed */
101     glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer);
102     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer);
103     glViewport(0, 0, edpy->width, edpy->height);
104     y1 = edpy->y_0_top ? edpy->height : 0;
105     y2 = edpy->y_0_top ? 0 : edpy->height;
106     glBlitFramebuffer(0, y1, edpy->width, y2,
107                       0, 0, edpy->width, edpy->height,
108                       GL_COLOR_BUFFER_BIT, GL_NEAREST);
109 
110     /* read pixels to surface */
111     glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer);
112     glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
113     glReadPixels(0, 0, edpy->width, edpy->height,
114                  GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds));
115 
116     /* notify about updates */
117     dpy_gfx_update(edpy->dcl.con, x, y, w, h);
118 }
119 
120 static const DisplayChangeListenerOps egl_ops = {
121     .dpy_name                = "egl-headless",
122     .dpy_refresh             = egl_refresh,
123     .dpy_gfx_update          = egl_gfx_update,
124     .dpy_gfx_switch          = egl_gfx_switch,
125 
126     .dpy_gl_ctx_create       = qemu_egl_create_context,
127     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
128     .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
129     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
130 
131     .dpy_gl_scanout_disable  = egl_scanout_disable,
132     .dpy_gl_scanout_texture  = egl_scanout_texture,
133     .dpy_gl_update           = egl_scanout_flush,
134 };
135 
136 void egl_headless_init(void)
137 {
138     QemuConsole *con;
139     egl_dpy *edpy;
140     int idx;
141 
142     if (egl_rendernode_init(NULL) < 0) {
143         error_report("egl: render node init failed");
144         exit(1);
145     }
146 
147     for (idx = 0;; idx++) {
148         con = qemu_console_lookup_by_index(idx);
149         if (!con || !qemu_console_is_graphic(con)) {
150             break;
151         }
152 
153         edpy = g_new0(egl_dpy, 1);
154         edpy->dcl.con = con;
155         edpy->dcl.ops = &egl_ops;
156         register_displaychangelistener(&edpy->dcl);
157     }
158 }
159