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