xref: /openbmc/qemu/ui/sdl2-gl.c (revision 31afe04586efeccb80cc36ffafcd0e32a3245ffb)
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 "ui/console.h"
30  #include "ui/input.h"
31  #include "ui/sdl2.h"
32  
33  static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout)
34  {
35      if (scon->scanout_mode == scanout) {
36          return;
37      }
38  
39      scon->scanout_mode = scanout;
40      if (!scon->scanout_mode) {
41          egl_fb_destroy(&scon->guest_fb);
42          if (scon->surface) {
43              surface_gl_destroy_texture(scon->gls, scon->surface);
44              surface_gl_create_texture(scon->gls, scon->surface);
45          }
46      }
47  }
48  
49  static void sdl2_gl_render_surface(struct sdl2_console *scon)
50  {
51      int ww, wh;
52  
53      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
54      sdl2_set_scanout_mode(scon, false);
55  
56      SDL_GetWindowSize(scon->real_window, &ww, &wh);
57      surface_gl_setup_viewport(scon->gls, scon->surface, ww, wh);
58  
59      surface_gl_render_texture(scon->gls, scon->surface);
60      SDL_GL_SwapWindow(scon->real_window);
61  }
62  
63  void sdl2_gl_update(DisplayChangeListener *dcl,
64                      int x, int y, int w, int h)
65  {
66      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
67  
68      assert(scon->opengl);
69  
70      if (!scon->real_window) {
71          return;
72      }
73  
74      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
75      surface_gl_update_texture(scon->gls, scon->surface, x, y, w, h);
76      scon->updates++;
77  }
78  
79  void sdl2_gl_switch(DisplayChangeListener *dcl,
80                      DisplaySurface *new_surface)
81  {
82      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
83      DisplaySurface *old_surface = scon->surface;
84  
85      assert(scon->opengl);
86  
87      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
88      surface_gl_destroy_texture(scon->gls, scon->surface);
89  
90      scon->surface = new_surface;
91  
92      if (is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) {
93          qemu_gl_fini_shader(scon->gls);
94          scon->gls = NULL;
95          sdl2_window_destroy(scon);
96          return;
97      }
98  
99      if (!scon->real_window) {
100          sdl2_window_create(scon);
101          scon->gls = qemu_gl_init_shader();
102      } else if (old_surface &&
103                 ((surface_width(old_surface)  != surface_width(new_surface)) ||
104                  (surface_height(old_surface) != surface_height(new_surface)))) {
105          sdl2_window_resize(scon);
106      }
107  
108      surface_gl_create_texture(scon->gls, scon->surface);
109  }
110  
111  void sdl2_gl_refresh(DisplayChangeListener *dcl)
112  {
113      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
114  
115      assert(scon->opengl);
116  
117      graphic_hw_update(dcl->con);
118      if (scon->updates && scon->real_window) {
119          scon->updates = 0;
120          sdl2_gl_render_surface(scon);
121      }
122      sdl2_poll_events(scon);
123  }
124  
125  void sdl2_gl_redraw(struct sdl2_console *scon)
126  {
127      assert(scon->opengl);
128  
129      if (scon->scanout_mode) {
130          /* sdl2_gl_scanout_flush actually only care about
131           * the first argument. */
132          return sdl2_gl_scanout_flush(&scon->dcl, 0, 0, 0, 0);
133      }
134      if (scon->surface) {
135          sdl2_gl_render_surface(scon);
136      }
137  }
138  
139  QEMUGLContext sdl2_gl_create_context(DisplayGLCtx *dgc,
140                                       QEMUGLParams *params)
141  {
142      struct sdl2_console *scon = container_of(dgc, struct sdl2_console, dgc);
143      SDL_GLContext ctx;
144  
145      assert(scon->opengl);
146  
147      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
148  
149      SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
150      if (scon->opts->gl == DISPLAYGL_MODE_ON ||
151          scon->opts->gl == DISPLAYGL_MODE_CORE) {
152          SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
153                              SDL_GL_CONTEXT_PROFILE_CORE);
154      } else if (scon->opts->gl == DISPLAYGL_MODE_ES) {
155          SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
156                              SDL_GL_CONTEXT_PROFILE_ES);
157      }
158      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver);
159      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver);
160  
161      ctx = SDL_GL_CreateContext(scon->real_window);
162  
163      /* If SDL fail to create a GL context and we use the "on" flag,
164       * then try to fallback to GLES.
165       */
166      if (!ctx && scon->opts->gl == DISPLAYGL_MODE_ON) {
167          SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
168                              SDL_GL_CONTEXT_PROFILE_ES);
169          ctx = SDL_GL_CreateContext(scon->real_window);
170      }
171      return (QEMUGLContext)ctx;
172  }
173  
174  void sdl2_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
175  {
176      SDL_GLContext sdlctx = (SDL_GLContext)ctx;
177  
178      SDL_GL_DeleteContext(sdlctx);
179  }
180  
181  int sdl2_gl_make_context_current(DisplayGLCtx *dgc,
182                                   QEMUGLContext ctx)
183  {
184      struct sdl2_console *scon = container_of(dgc, struct sdl2_console, dgc);
185      SDL_GLContext sdlctx = (SDL_GLContext)ctx;
186  
187      assert(scon->opengl);
188  
189      return SDL_GL_MakeCurrent(scon->real_window, sdlctx);
190  }
191  
192  void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
193  {
194      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
195  
196      assert(scon->opengl);
197      scon->w = 0;
198      scon->h = 0;
199      sdl2_set_scanout_mode(scon, false);
200  }
201  
202  void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
203                               uint32_t backing_id,
204                               bool backing_y_0_top,
205                               uint32_t backing_width,
206                               uint32_t backing_height,
207                               uint32_t x, uint32_t y,
208                               uint32_t w, uint32_t h)
209  {
210      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
211  
212      assert(scon->opengl);
213      scon->x = x;
214      scon->y = y;
215      scon->w = w;
216      scon->h = h;
217      scon->y0_top = backing_y_0_top;
218  
219      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
220  
221      sdl2_set_scanout_mode(scon, true);
222      egl_fb_setup_for_tex(&scon->guest_fb, backing_width, backing_height,
223                           backing_id, false);
224  }
225  
226  void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
227                             uint32_t x, uint32_t y, uint32_t w, uint32_t h)
228  {
229      struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
230      int ww, wh;
231  
232      assert(scon->opengl);
233      if (!scon->scanout_mode) {
234          return;
235      }
236      if (!scon->guest_fb.framebuffer) {
237          return;
238      }
239  
240      SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
241  
242      SDL_GetWindowSize(scon->real_window, &ww, &wh);
243      egl_fb_setup_default(&scon->win_fb, ww, wh);
244      egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top);
245  
246      SDL_GL_SwapWindow(scon->real_window);
247  }
248