xref: /qemu/ui/sdl2-gl.c (revision 7a4e543d)
1 /*
2  * QEMU SDL display driver -- opengl support
3  *
4  * Copyright (c) 2014 Red Hat
5  *
6  * Authors:
7  *     Gerd Hoffmann <kraxel@redhat.com>
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 
28 #include "qemu/osdep.h"
29 #include "qemu-common.h"
30 #include "ui/console.h"
31 #include "ui/input.h"
32 #include "ui/sdl2.h"
33 #include "sysemu/sysemu.h"
34 
35 #include <epoxy/gl.h>
36 
37 static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout)
38 {
39     if (scon->scanout_mode == scanout) {
40         return;
41     }
42 
43     scon->scanout_mode = scanout;
44     if (!scon->scanout_mode) {
45         if (scon->fbo_id) {
46             glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
47                                       GL_COLOR_ATTACHMENT0_EXT,
48                                       GL_TEXTURE_2D, 0, 0);
49             glDeleteFramebuffers(1, &scon->fbo_id);
50             glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
51             scon->fbo_id = 0;
52         }
53         if (scon->surface) {
54             surface_gl_destroy_texture(scon->gls, scon->surface);
55             surface_gl_create_texture(scon->gls, scon->surface);
56         }
57     }
58 }
59 
60 static void sdl2_gl_render_surface(struct sdl2_console *scon)
61 {
62     int ww, wh;
63 
64     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
65     sdl2_set_scanout_mode(scon, false);
66 
67     SDL_GetWindowSize(scon->real_window, &ww, &wh);
68     surface_gl_setup_viewport(scon->gls, scon->surface, ww, wh);
69 
70     surface_gl_render_texture(scon->gls, scon->surface);
71     SDL_GL_SwapWindow(scon->real_window);
72 }
73 
74 void sdl2_gl_update(DisplayChangeListener *dcl,
75                     int x, int y, int w, int h)
76 {
77     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
78 
79     assert(scon->opengl);
80 
81     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
82     surface_gl_update_texture(scon->gls, scon->surface, x, y, w, h);
83     scon->updates++;
84 }
85 
86 void sdl2_gl_switch(DisplayChangeListener *dcl,
87                     DisplaySurface *new_surface)
88 {
89     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
90     DisplaySurface *old_surface = scon->surface;
91 
92     assert(scon->opengl);
93 
94     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
95     surface_gl_destroy_texture(scon->gls, scon->surface);
96 
97     scon->surface = new_surface;
98 
99     if (!new_surface) {
100         console_gl_fini_context(scon->gls);
101         scon->gls = NULL;
102         sdl2_window_destroy(scon);
103         return;
104     }
105 
106     if (!scon->real_window) {
107         sdl2_window_create(scon);
108         scon->gls = console_gl_init_context();
109     } else if (old_surface &&
110                ((surface_width(old_surface)  != surface_width(new_surface)) ||
111                 (surface_height(old_surface) != surface_height(new_surface)))) {
112         sdl2_window_resize(scon);
113     }
114 
115     surface_gl_create_texture(scon->gls, scon->surface);
116 }
117 
118 void sdl2_gl_refresh(DisplayChangeListener *dcl)
119 {
120     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
121 
122     assert(scon->opengl);
123 
124     graphic_hw_update(dcl->con);
125     if (scon->updates && scon->surface) {
126         scon->updates = 0;
127         sdl2_gl_render_surface(scon);
128     }
129     sdl2_poll_events(scon);
130 }
131 
132 void sdl2_gl_redraw(struct sdl2_console *scon)
133 {
134     assert(scon->opengl);
135 
136     if (scon->surface) {
137         sdl2_gl_render_surface(scon);
138     }
139 }
140 
141 QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
142                                      QEMUGLParams *params)
143 {
144     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
145     SDL_GLContext ctx;
146 
147     assert(scon->opengl);
148 
149     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
150 
151     SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
152     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
153                         SDL_GL_CONTEXT_PROFILE_CORE);
154     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver);
155     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver);
156 
157     ctx = SDL_GL_CreateContext(scon->real_window);
158     return (QEMUGLContext)ctx;
159 }
160 
161 void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
162 {
163     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
164 
165     SDL_GL_DeleteContext(sdlctx);
166 }
167 
168 int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
169                                  QEMUGLContext ctx)
170 {
171     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
172     SDL_GLContext sdlctx = (SDL_GLContext)ctx;
173 
174     assert(scon->opengl);
175 
176     return SDL_GL_MakeCurrent(scon->real_window, sdlctx);
177 }
178 
179 QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl)
180 {
181     SDL_GLContext sdlctx;
182 
183     sdlctx = SDL_GL_GetCurrentContext();
184     return (QEMUGLContext)sdlctx;
185 }
186 
187 void sdl2_gl_scanout(DisplayChangeListener *dcl,
188                      uint32_t backing_id, bool backing_y_0_top,
189                      uint32_t x, uint32_t y,
190                      uint32_t w, uint32_t h)
191 {
192     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
193 
194     assert(scon->opengl);
195     scon->x = x;
196     scon->y = y;
197     scon->w = w;
198     scon->h = h;
199     scon->tex_id = backing_id;
200     scon->y0_top = backing_y_0_top;
201 
202     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
203 
204     if (scon->tex_id == 0 || scon->w == 0 || scon->h == 0) {
205         sdl2_set_scanout_mode(scon, false);
206         return;
207     }
208 
209     sdl2_set_scanout_mode(scon, true);
210     if (!scon->fbo_id) {
211         glGenFramebuffers(1, &scon->fbo_id);
212     }
213 
214     glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
215     glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
216                               GL_TEXTURE_2D, scon->tex_id, 0);
217 }
218 
219 void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
220                            uint32_t x, uint32_t y, uint32_t w, uint32_t h)
221 {
222     struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
223     int ww, wh, y1, y2;
224 
225     assert(scon->opengl);
226     if (!scon->scanout_mode) {
227         return;
228     }
229     if (!scon->fbo_id) {
230         return;
231     }
232 
233     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
234 
235     glBindFramebuffer(GL_READ_FRAMEBUFFER, scon->fbo_id);
236     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
237 
238     SDL_GetWindowSize(scon->real_window, &ww, &wh);
239     glViewport(0, 0, ww, wh);
240     y1 = scon->y0_top ? 0 : scon->h;
241     y2 = scon->y0_top ? scon->h : 0;
242     glBlitFramebuffer(0, y1, scon->w, y2,
243                       0, 0, ww, wh,
244                       GL_COLOR_BUFFER_BIT, GL_NEAREST);
245     glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
246 
247     SDL_GL_SwapWindow(scon->real_window);
248 }
249