xref: /openbmc/qemu/ui/gtk-gl-area.c (revision 96d885b9)
1 /*
2  * GTK UI -- glarea opengl code.
3  *
4  * Requires 3.16+ (GtkGLArea widget).
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu-common.h"
11 
12 #include "trace.h"
13 
14 #include "ui/console.h"
15 #include "ui/gtk.h"
16 #include "ui/egl-helpers.h"
17 
18 #include "sysemu/sysemu.h"
19 
20 static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
21 {
22     if (vc->gfx.scanout_mode == scanout) {
23         return;
24     }
25 
26     vc->gfx.scanout_mode = scanout;
27     if (!vc->gfx.scanout_mode) {
28         if (vc->gfx.fbo_id) {
29             glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
30                                       GL_COLOR_ATTACHMENT0_EXT,
31                                       GL_TEXTURE_2D, 0, 0);
32             glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
33             glDeleteFramebuffers(1, &vc->gfx.fbo_id);
34             vc->gfx.fbo_id = 0;
35         }
36         if (vc->gfx.surface) {
37             surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
38             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
39         }
40     }
41 }
42 
43 /** DisplayState Callbacks (opengl version) **/
44 
45 void gd_gl_area_draw(VirtualConsole *vc)
46 {
47     int ww, wh, y1, y2;
48 
49     if (!vc->gfx.gls) {
50         return;
51     }
52 
53     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
54     ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
55     wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
56 
57     if (vc->gfx.scanout_mode) {
58         if (!vc->gfx.fbo_id) {
59             return;
60         }
61 
62         glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
63         /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
64 
65         glViewport(0, 0, ww, wh);
66         y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
67         y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
68         glBlitFramebuffer(0, y1, vc->gfx.w, y2,
69                           0, 0, ww, wh,
70                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
71     } else {
72         if (!vc->gfx.ds) {
73             return;
74         }
75         gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
76 
77         surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
78         surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
79     }
80 }
81 
82 void gd_gl_area_update(DisplayChangeListener *dcl,
83                    int x, int y, int w, int h)
84 {
85     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
86 
87     if (!vc->gfx.gls || !vc->gfx.ds) {
88         return;
89     }
90 
91     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
92     surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
93     vc->gfx.glupdates++;
94 }
95 
96 void gd_gl_area_refresh(DisplayChangeListener *dcl)
97 {
98     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
99 
100     if (!vc->gfx.gls) {
101         if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
102             return;
103         }
104         gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
105         vc->gfx.gls = console_gl_init_context();
106         if (vc->gfx.ds) {
107             surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
108         }
109     }
110 
111     graphic_hw_update(dcl->con);
112 
113     if (vc->gfx.glupdates) {
114         vc->gfx.glupdates = 0;
115         gtk_gl_area_set_scanout_mode(vc, false);
116         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
117     }
118 }
119 
120 void gd_gl_area_switch(DisplayChangeListener *dcl,
121                        DisplaySurface *surface)
122 {
123     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
124     bool resized = true;
125 
126     trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
127 
128     if (vc->gfx.ds &&
129         surface_width(vc->gfx.ds) == surface_width(surface) &&
130         surface_height(vc->gfx.ds) == surface_height(surface)) {
131         resized = false;
132     }
133 
134     if (vc->gfx.gls) {
135         gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
136         surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
137         surface_gl_create_texture(vc->gfx.gls, surface);
138     }
139     vc->gfx.ds = surface;
140 
141     if (resized) {
142         gd_update_windowsize(vc);
143     }
144 }
145 
146 QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
147                                         QEMUGLParams *params)
148 {
149     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
150     GdkWindow *window;
151     GdkGLContext *ctx;
152     GError *err = NULL;
153 
154     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
155     window = gtk_widget_get_window(vc->gfx.drawing_area);
156     ctx = gdk_window_create_gl_context(window, &err);
157     gdk_gl_context_set_required_version(ctx,
158                                         params->major_ver,
159                                         params->minor_ver);
160     gdk_gl_context_realize(ctx, &err);
161     return ctx;
162 }
163 
164 void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
165 {
166     /* FIXME */
167 }
168 
169 void gd_gl_area_scanout(DisplayChangeListener *dcl,
170                         uint32_t backing_id, bool backing_y_0_top,
171                         uint32_t x, uint32_t y,
172                         uint32_t w, uint32_t h)
173 {
174     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
175 
176     vc->gfx.x = x;
177     vc->gfx.y = y;
178     vc->gfx.w = w;
179     vc->gfx.h = h;
180     vc->gfx.tex_id = backing_id;
181     vc->gfx.y0_top = backing_y_0_top;
182 
183     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
184 
185     if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
186         gtk_gl_area_set_scanout_mode(vc, false);
187         return;
188     }
189 
190     gtk_gl_area_set_scanout_mode(vc, true);
191     if (!vc->gfx.fbo_id) {
192         glGenFramebuffers(1, &vc->gfx.fbo_id);
193     }
194 
195     glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
196     glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
197                               GL_TEXTURE_2D, vc->gfx.tex_id, 0);
198 }
199 
200 void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
201                           uint32_t x, uint32_t y, uint32_t w, uint32_t h)
202 {
203     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
204 
205     gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
206 }
207 
208 void gtk_gl_area_init(void)
209 {
210     display_opengl = 1;
211 }
212 
213 QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl)
214 {
215     return gdk_gl_context_get_current();
216 }
217 
218 int gd_gl_area_make_current(DisplayChangeListener *dcl,
219                             QEMUGLContext ctx)
220 {
221     gdk_gl_context_make_current(ctx);
222     return 0;
223 }
224