diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2015-05-05 14:06:12 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2015-05-05 14:06:12 +0100 |
commit | 874e9aeeeb74c5459639a93439a502d262847e68 (patch) | |
tree | 549b6cc3ff9f8842320775e7e51f61d182f4aadb | |
parent | b4c5df7a15dad2417bc05d08a470b82ab89d56ea (diff) | |
parent | e444ea34f8ec27acfa9ead7eaa9904238c831e69 (diff) |
Merge remote-tracking branch 'remotes/kraxel/tags/pull-sdl-20150505-1' into staging
sdl2: add opengl support
# gpg: Signature made Tue May 5 10:36:25 2015 BST using RSA key ID D3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
* remotes/kraxel/tags/pull-sdl-20150505-1:
sdl2: Fix RGB555
sdl2: add support for display rendering using opengl.
sdl2: move SDL_* includes to sdl2.h
console-gl: add opengl rendering helper functions
opengl: add shader helper functions.
opengl: add shader build infrastructure
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | Makefile | 17 | ||||
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | include/ui/console.h | 37 | ||||
-rw-r--r-- | include/ui/sdl2.h | 17 | ||||
-rw-r--r-- | include/ui/shader.h | 11 | ||||
-rw-r--r-- | scripts/shaderinclude.pl | 16 | ||||
-rw-r--r-- | ui/Makefile.objs | 13 | ||||
-rw-r--r-- | ui/console-gl.c | 168 | ||||
-rw-r--r-- | ui/sdl.c | 10 | ||||
-rw-r--r-- | ui/sdl2-2d.c | 28 | ||||
-rw-r--r-- | ui/sdl2-gl.c | 112 | ||||
-rw-r--r-- | ui/sdl2-input.c | 6 | ||||
-rw-r--r-- | ui/sdl2.c | 71 | ||||
-rw-r--r-- | ui/shader.c | 114 | ||||
-rw-r--r-- | ui/shader/texture-blit.frag | 10 | ||||
-rw-r--r-- | ui/shader/texture-blit.vert | 10 | ||||
-rw-r--r-- | vl.c | 23 |
17 files changed, 637 insertions, 28 deletions
@@ -296,6 +296,7 @@ clean: rm -f fsdev/*.pod rm -rf .libs */.libs rm -f qemu-img-cmds.h + rm -f ui/shader/*-vert.h ui/shader/*-frag.h @# May not be present in GENERATED_HEADERS rm -f trace/generated-tracers-dtrace.dtrace* rm -f trace/generated-tracers-dtrace.h* @@ -441,6 +442,22 @@ cscope: find "$(SRC_PATH)" -name "*.[chsS]" -print | sed 's,^\./,,' > ./cscope.files cscope -b +# opengl shader programs +ui/shader/%-vert.h: $(SRC_PATH)/ui/shader/%.vert $(SRC_PATH)/scripts/shaderinclude.pl + @mkdir -p $(dir $@) + $(call quiet-command,\ + perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ + " VERT $@") + +ui/shader/%-frag.h: $(SRC_PATH)/ui/shader/%.frag $(SRC_PATH)/scripts/shaderinclude.pl + @mkdir -p $(dir $@) + $(call quiet-command,\ + perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ + " FRAG $@") + +ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \ + ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h + # documentation MAKEINFO=makeinfo MAKEINFOFLAGS=--no-headers --no-split --number-sections @@ -3142,7 +3142,7 @@ else fi if test "$opengl" != "no" ; then - opengl_pkgs="gl" + opengl_pkgs="gl glesv2" if $pkg_config $opengl_pkgs x11 && test "$have_glx" = "yes"; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags" opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs" diff --git a/include/ui/console.h b/include/ui/console.h index 03cd665a8f..0b7589600b 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -9,6 +9,11 @@ #include "qapi-types.h" #include "qapi/error.h" +#ifdef CONFIG_OPENGL +# include <GLES2/gl2.h> +# include <GLES2/gl2ext.h> +#endif + /* keyboard/mouse support */ #define MOUSE_EVENT_LBUTTON 0x01 @@ -117,6 +122,11 @@ struct DisplaySurface { pixman_format_code_t format; pixman_image_t *image; uint8_t flags; +#ifdef CONFIG_OPENGL + GLenum glformat; + GLenum gltype; + GLuint texture; +#endif }; typedef struct QemuUIInfo { @@ -270,6 +280,11 @@ static inline int surface_bytes_per_pixel(DisplaySurface *s) return (bits + 7) / 8; } +static inline pixman_format_code_t surface_format(DisplaySurface *s) +{ + return s->format; +} + #ifdef CONFIG_CURSES #include <curses.h> typedef chtype console_ch_t; @@ -322,7 +337,29 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y, int dst_x, int dst_y, int w, int h); DisplaySurface *qemu_console_surface(QemuConsole *con); +/* console-gl.c */ +typedef struct ConsoleGLState ConsoleGLState; +#ifdef CONFIG_OPENGL +ConsoleGLState *console_gl_init_context(void); +void console_gl_fini_context(ConsoleGLState *gls); +bool console_gl_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format); +void surface_gl_create_texture(ConsoleGLState *gls, + DisplaySurface *surface); +void surface_gl_update_texture(ConsoleGLState *gls, + DisplaySurface *surface, + int x, int y, int w, int h); +void surface_gl_render_texture(ConsoleGLState *gls, + DisplaySurface *surface); +void surface_gl_destroy_texture(ConsoleGLState *gls, + DisplaySurface *surface); +void surface_gl_setup_viewport(ConsoleGLState *gls, + DisplaySurface *surface, + int ww, int wh); +#endif + /* sdl.c */ +void sdl_display_early_init(int opengl); void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); /* cocoa.m */ diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 51fff2e9b8..2fdad8f300 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -1,6 +1,12 @@ #ifndef SDL2_H #define SDL2_H +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include <SDL.h> +#include <SDL_syswm.h> + struct sdl2_console { DisplayChangeListener dcl; DisplaySurface *surface; @@ -11,6 +17,10 @@ struct sdl2_console { int last_vm_running; /* per console for caption reasons */ int x, y; int hidden; + int opengl; + int updates; + SDL_GLContext winctx; + ConsoleGLState *gls; }; void sdl2_window_create(struct sdl2_console *scon); @@ -31,4 +41,11 @@ void sdl2_2d_redraw(struct sdl2_console *scon); bool sdl2_2d_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); +void sdl2_gl_update(DisplayChangeListener *dcl, + int x, int y, int w, int h); +void sdl2_gl_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface); +void sdl2_gl_refresh(DisplayChangeListener *dcl); +void sdl2_gl_redraw(struct sdl2_console *scon); + #endif /* SDL2_H */ diff --git a/include/ui/shader.h b/include/ui/shader.h new file mode 100644 index 0000000000..1ff926c9e1 --- /dev/null +++ b/include/ui/shader.h @@ -0,0 +1,11 @@ +#ifdef CONFIG_OPENGL +# include <GLES2/gl2.h> +# include <GLES2/gl2ext.h> +#endif + +void qemu_gl_run_texture_blit(GLint texture_blit_prog); + +GLuint qemu_gl_create_compile_shader(GLenum type, const GLchar *src); +GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag); +GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src, + const GLchar *frag_src); diff --git a/scripts/shaderinclude.pl b/scripts/shaderinclude.pl new file mode 100644 index 0000000000..81b5146332 --- /dev/null +++ b/scripts/shaderinclude.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl +use strict; +use warnings; + +my $file = shift; +open FILE, "<", $file or die "open $file: $!"; +my $name = $file; +$name =~ s|.*/||; +$name =~ s/[-.]/_/g; +print "static GLchar ${name}_src[] =\n"; +while (<FILE>) { + chomp; + printf " \"%s\\n\"\n", $_; +} +print " \"\\n\";\n"; +close FILE; diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 13b5cfbe41..029a42a688 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -21,7 +21,20 @@ sdl.mo-objs := sdl.o sdl_zoom.o endif ifeq ($(CONFIG_SDLABI),2.0) sdl.mo-objs := sdl2.o sdl2-input.o sdl2-2d.o +ifeq ($(CONFIG_OPENGL),y) +sdl.mo-objs += sdl2-gl.o +endif endif sdl.mo-cflags := $(SDL_CFLAGS) +ifeq ($(CONFIG_OPENGL),y) +common-obj-y += shader.o +common-obj-y += console-gl.o +endif + gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) +shader.o-cflags += $(OPENGL_CFLAGS) +console-gl.o-cflags += $(OPENGL_CFLAGS) + +shader.o-libs += $(OPENGL_LIBS) +console-gl.o-libs += $(OPENGL_LIBS) diff --git a/ui/console-gl.c b/ui/console-gl.c new file mode 100644 index 0000000000..cb45cf8a29 --- /dev/null +++ b/ui/console-gl.c @@ -0,0 +1,168 @@ +/* + * QEMU graphical console -- opengl helper bits + * + * Copyright (c) 2014 Red Hat + * + * Authors: + * Gerd Hoffmann <kraxel@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/shader.h" + +#include "shader/texture-blit-vert.h" +#include "shader/texture-blit-frag.h" + +struct ConsoleGLState { + GLint texture_blit_prog; +}; + +/* ---------------------------------------------------------------------- */ + +ConsoleGLState *console_gl_init_context(void) +{ + ConsoleGLState *gls = g_new0(ConsoleGLState, 1); + + gls->texture_blit_prog = qemu_gl_create_compile_link_program + (texture_blit_vert_src, texture_blit_frag_src); + if (!gls->texture_blit_prog) { + exit(1); + } + + return gls; +} + +void console_gl_fini_context(ConsoleGLState *gls) +{ + if (!gls) { + return; + } + g_free(gls); +} + +bool console_gl_check_format(DisplayChangeListener *dcl, + pixman_format_code_t format) +{ + switch (format) { + case PIXMAN_BE_b8g8r8x8: + case PIXMAN_BE_b8g8r8a8: + case PIXMAN_r5g6b5: + return true; + default: + return false; + } +} + +void surface_gl_create_texture(ConsoleGLState *gls, + DisplaySurface *surface) +{ + assert(gls); + assert(surface_stride(surface) % surface_bytes_per_pixel(surface) == 0); + + switch (surface->format) { + case PIXMAN_BE_b8g8r8x8: + case PIXMAN_BE_b8g8r8a8: + surface->glformat = GL_BGRA_EXT; + surface->gltype = GL_UNSIGNED_BYTE; + break; + case PIXMAN_r5g6b5: + surface->glformat = GL_RGB; + surface->gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + default: + g_assert_not_reached(); + } + + glGenTextures(1, &surface->texture); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, surface->texture); + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, + surface_stride(surface) / surface_bytes_per_pixel(surface)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + surface_width(surface), + surface_height(surface), + 0, surface->glformat, surface->gltype, + surface_data(surface)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void surface_gl_update_texture(ConsoleGLState *gls, + DisplaySurface *surface, + int x, int y, int w, int h) +{ + uint8_t *data = (void *)surface_data(surface); + + assert(gls); + + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, + surface_stride(surface) / surface_bytes_per_pixel(surface)); + glTexSubImage2D(GL_TEXTURE_2D, 0, + x, y, w, h, + surface->glformat, surface->gltype, + data + surface_stride(surface) * y + + surface_bytes_per_pixel(surface) * x); +} + +void surface_gl_render_texture(ConsoleGLState *gls, + DisplaySurface *surface) +{ + assert(gls); + + glClearColor(0.1f, 0.1f, 0.1f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + qemu_gl_run_texture_blit(gls->texture_blit_prog); +} + +void surface_gl_destroy_texture(ConsoleGLState *gls, + DisplaySurface *surface) +{ + if (!surface || !surface->texture) { + return; + } + glDeleteTextures(1, &surface->texture); + surface->texture = 0; +} + +void surface_gl_setup_viewport(ConsoleGLState *gls, + DisplaySurface *surface, + int ww, int wh) +{ + int gw, gh, stripe; + float sw, sh; + + assert(gls); + + gw = surface_width(surface); + gh = surface_height(surface); + + sw = (float)ww/gw; + sh = (float)wh/gh; + if (sw < sh) { + stripe = wh - wh*sw/sh; + glViewport(0, stripe / 2, ww, wh - stripe); + } else { + stripe = ww - ww*sh/sw; + glViewport(stripe / 2, 0, ww - stripe, wh); + } +} @@ -908,6 +908,16 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_cursor_define = sdl_mouse_define, }; +void sdl_display_early_init(int opengl) +{ + if (opengl == 1 /* on */) { + fprintf(stderr, + "SDL1 display code has no opengl support.\n" + "Please recompile qemu with SDL2, using\n" + "./configure --enable-sdl --with-sdlabi=2.0\n"); + } +} + void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) { int flags; diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index f907c21895..d0b340f956 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -23,12 +23,6 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ -/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ -#undef WIN32_LEAN_AND_MEAN - -#include <SDL.h> -#include <SDL_syswm.h> - #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -42,6 +36,8 @@ void sdl2_2d_update(DisplayChangeListener *dcl, DisplaySurface *surf = qemu_console_surface(dcl->con); SDL_Rect rect; + assert(!scon->opengl); + if (!surf) { return; } @@ -67,6 +63,8 @@ void sdl2_2d_switch(DisplayChangeListener *dcl, DisplaySurface *old_surface = scon->surface; int format = 0; + assert(!scon->opengl); + scon->surface = new_surface; if (scon->texture) { @@ -91,10 +89,21 @@ void sdl2_2d_switch(DisplayChangeListener *dcl, surface_width(new_surface), surface_height(new_surface)); - if (surface_bits_per_pixel(scon->surface) == 16) { + switch (surface_format(scon->surface)) { + case PIXMAN_x1r5g5b5: + format = SDL_PIXELFORMAT_ARGB1555; + break; + case PIXMAN_r5g6b5: format = SDL_PIXELFORMAT_RGB565; - } else if (surface_bits_per_pixel(scon->surface) == 32) { + break; + case PIXMAN_x8r8g8b8: format = SDL_PIXELFORMAT_ARGB8888; + break; + case PIXMAN_r8g8b8x8: + format = SDL_PIXELFORMAT_RGBA8888; + break; + default: + g_assert_not_reached(); } scon->texture = SDL_CreateTexture(scon->real_renderer, format, SDL_TEXTUREACCESS_STREAMING, @@ -107,12 +116,15 @@ void sdl2_2d_refresh(DisplayChangeListener *dcl) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + assert(!scon->opengl); graphic_hw_update(dcl->con); sdl2_poll_events(scon); } void sdl2_2d_redraw(struct sdl2_console *scon) { + assert(!scon->opengl); + if (!scon->surface) { return; } diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c new file mode 100644 index 0000000000..b604c0671e --- /dev/null +++ b/ui/sdl2-gl.c @@ -0,0 +1,112 @@ +/* + * QEMU SDL display driver -- opengl support + * + * Copyright (c) 2014 Red Hat + * + * Authors: + * Gerd Hoffmann <kraxel@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/input.h" +#include "ui/sdl2.h" +#include "sysemu/sysemu.h" + +static void sdl2_gl_render_surface(struct sdl2_console *scon) +{ + int ww, wh; + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + + SDL_GetWindowSize(scon->real_window, &ww, &wh); + surface_gl_setup_viewport(scon->gls, scon->surface, ww, wh); + + surface_gl_render_texture(scon->gls, scon->surface); + SDL_GL_SwapWindow(scon->real_window); +} + +void sdl2_gl_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + + assert(scon->opengl); + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + surface_gl_update_texture(scon->gls, scon->surface, x, y, w, h); + scon->updates++; +} + +void sdl2_gl_switch(DisplayChangeListener *dcl, + DisplaySurface *new_surface) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + DisplaySurface *old_surface = scon->surface; + + assert(scon->opengl); + + SDL_GL_MakeCurrent(scon->real_window, scon->winctx); + surface_gl_destroy_texture(scon->gls, scon->surface); + + scon->surface = new_surface; + + if (!new_surface) { + console_gl_fini_context(scon->gls); + scon->gls = NULL; + sdl2_window_destroy(scon); + return; + } + + if (!scon->real_window) { + sdl2_window_create(scon); + scon->gls = console_gl_init_context(); + } else if (old_surface && + ((surface_width(old_surface) != surface_width(new_surface)) || + (surface_height(old_surface) != surface_height(new_surface)))) { + sdl2_window_resize(scon); + } + + surface_gl_create_texture(scon->gls, scon->surface); +} + +void sdl2_gl_refresh(DisplayChangeListener *dcl) +{ + struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); + + assert(scon->opengl); + + graphic_hw_update(dcl->con); + if (scon->updates && scon->surface) { + scon->updates = 0; + sdl2_gl_render_surface(scon); + } + sdl2_poll_events(scon); +} + +void sdl2_gl_redraw(struct sdl2_console *scon) +{ + assert(scon->opengl); + + if (scon->surface) { + sdl2_gl_render_surface(scon); + } +} diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index a1973fc2e0..ac5dc9476b 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -23,12 +23,6 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ -/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ -#undef WIN32_LEAN_AND_MEAN - -#include <SDL.h> -#include <SDL_syswm.h> - #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -23,12 +23,6 @@ */ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ -/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ -#undef WIN32_LEAN_AND_MEAN - -#include <SDL.h> -#include <SDL_syswm.h> - #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -92,6 +86,9 @@ void sdl2_window_create(struct sdl2_console *scon) surface_height(scon->surface), flags); scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); + if (scon->opengl) { + scon->winctx = SDL_GL_GetCurrentContext(); + } sdl_update_caption(scon); } @@ -118,6 +115,17 @@ void sdl2_window_resize(struct sdl2_console *scon) surface_height(scon->surface)); } +static void sdl2_redraw(struct sdl2_console *scon) +{ + if (scon->opengl) { +#ifdef CONFIG_OPENGL + sdl2_gl_redraw(scon); +#endif + } else { + sdl2_2d_redraw(scon); + } +} + static void sdl_update_caption(struct sdl2_console *scon) { char win_title[1024]; @@ -316,7 +324,7 @@ static void toggle_full_screen(struct sdl2_console *scon) } SDL_SetWindowFullscreen(scon->real_window, 0); } - sdl2_2d_redraw(scon); + sdl2_redraw(scon); } static void handle_keydown(SDL_Event *ev) @@ -364,8 +372,10 @@ static void handle_keydown(SDL_Event *ev) case SDL_SCANCODE_U: sdl2_window_destroy(scon); sdl2_window_create(scon); - /* re-create texture */ - sdl2_2d_switch(&scon->dcl, scon->surface); + if (!scon->opengl) { + /* re-create scon->texture */ + sdl2_2d_switch(&scon->dcl, scon->surface); + } gui_keysym = 1; break; #if 0 @@ -384,7 +394,7 @@ static void handle_keydown(SDL_Event *ev) fprintf(stderr, "%s: scale to %dx%d\n", __func__, width, height); sdl_scale(scon, width, height); - sdl2_2d_redraw(scon); + sdl2_redraw(scon); gui_keysym = 1; } #endif @@ -520,10 +530,10 @@ static void handle_windowevent(SDL_Event *ev) info.height = ev->window.data2; dpy_set_ui_info(scon->dcl.con, &info); } - sdl2_2d_redraw(scon); + sdl2_redraw(scon); break; case SDL_WINDOWEVENT_EXPOSED: - sdl2_2d_redraw(scon); + sdl2_redraw(scon); break; case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_ENTER: @@ -677,6 +687,35 @@ static const DisplayChangeListenerOps dcl_2d_ops = { .dpy_cursor_define = sdl_mouse_define, }; +#ifdef CONFIG_OPENGL +static const DisplayChangeListenerOps dcl_gl_ops = { + .dpy_name = "sdl2-gl", + .dpy_gfx_update = sdl2_gl_update, + .dpy_gfx_switch = sdl2_gl_switch, + .dpy_gfx_check_format = console_gl_check_format, + .dpy_refresh = sdl2_gl_refresh, + .dpy_mouse_set = sdl_mouse_warp, + .dpy_cursor_define = sdl_mouse_define, +}; +#endif + +void sdl_display_early_init(int opengl) +{ + switch (opengl) { + case -1: /* default */ + case 0: /* off */ + break; + case 1: /* on */ +#ifdef CONFIG_OPENGL + display_opengl = 1; +#endif + break; + default: + g_assert_not_reached(); + break; + } +} + void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) { int flags; @@ -722,10 +761,16 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) if (!qemu_console_is_graphic(con)) { sdl2_console[i].hidden = true; } + sdl2_console[i].idx = i; +#ifdef CONFIG_OPENGL + sdl2_console[i].opengl = display_opengl; + sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops; +#else + sdl2_console[i].opengl = 0; sdl2_console[i].dcl.ops = &dcl_2d_ops; +#endif sdl2_console[i].dcl.con = con; register_displaychangelistener(&sdl2_console[i].dcl); - sdl2_console[i].idx = i; } /* Load a 32x32x4 image. White pixels are transparent. */ diff --git a/ui/shader.c b/ui/shader.c new file mode 100644 index 0000000000..52a4632930 --- /dev/null +++ b/ui/shader.c @@ -0,0 +1,114 @@ +/* + * QEMU opengl shader helper functions + * + * Copyright (c) 2014 Red Hat + * + * Authors: + * Gerd Hoffmann <kraxel@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu-common.h" +#include "ui/shader.h" + +/* ---------------------------------------------------------------------- */ + +void qemu_gl_run_texture_blit(GLint texture_blit_prog) +{ + GLfloat in_position[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1, + }; + GLint l_position; + + glUseProgram(texture_blit_prog); + l_position = glGetAttribLocation(texture_blit_prog, "in_position"); + glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, in_position); + glEnableVertexAttribArray(l_position); + glDrawArrays(GL_TRIANGLE_STRIP, l_position, 4); +} + +/* ---------------------------------------------------------------------- */ + +GLuint qemu_gl_create_compile_shader(GLenum type, const GLchar *src) +{ + GLuint shader; + GLint status, length; + char *errmsg; + + shader = glCreateShader(type); + glShaderSource(shader, 1, &src, 0); + glCompileShader(shader); + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status) { + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + errmsg = malloc(length); + glGetShaderInfoLog(shader, length, &length, errmsg); + fprintf(stderr, "%s: compile %s error\n%s\n", __func__, + (type == GL_VERTEX_SHADER) ? "vertex" : "fragment", + errmsg); + free(errmsg); + return 0; + } + return shader; +} + +GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag) +{ + GLuint program; + GLint status, length; + char *errmsg; + + program = glCreateProgram(); + glAttachShader(program, vert); + glAttachShader(program, frag); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); + errmsg = malloc(length); + glGetProgramInfoLog(program, length, &length, errmsg); + fprintf(stderr, "%s: link program: %s\n", __func__, errmsg); + free(errmsg); + return 0; + } + return program; +} + +GLuint qemu_gl_create_compile_link_program(const GLchar *vert_src, + const GLchar *frag_src) +{ + GLuint vert_shader, frag_shader, program; + + vert_shader = qemu_gl_create_compile_shader(GL_VERTEX_SHADER, vert_src); + frag_shader = qemu_gl_create_compile_shader(GL_FRAGMENT_SHADER, frag_src); + if (!vert_shader || !frag_shader) { + return 0; + } + + program = qemu_gl_create_link_program(vert_shader, frag_shader); + glDeleteShader(vert_shader); + glDeleteShader(frag_shader); + + return program; +} diff --git a/ui/shader/texture-blit.frag b/ui/shader/texture-blit.frag new file mode 100644 index 0000000000..bfa202c22b --- /dev/null +++ b/ui/shader/texture-blit.frag @@ -0,0 +1,10 @@ + +#version 300 es + +uniform sampler2D image; +in mediump vec2 ex_tex_coord; +out mediump vec4 out_frag_color; + +void main(void) { + out_frag_color = texture(image, ex_tex_coord); +} diff --git a/ui/shader/texture-blit.vert b/ui/shader/texture-blit.vert new file mode 100644 index 0000000000..6fe2744d68 --- /dev/null +++ b/ui/shader/texture-blit.vert @@ -0,0 +1,10 @@ + +#version 300 es + +in vec2 in_position; +out vec2 ex_tex_coord; + +void main(void) { + gl_Position = vec4(in_position, 0.0, 1.0); + ex_tex_coord = vec2(1.0 + in_position.x, 1.0 - in_position.y) * 0.5; +} @@ -130,6 +130,7 @@ static int data_dir_idx; const char *bios_name = NULL; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; DisplayType display_type = DT_DEFAULT; +int request_opengl = -1; int display_opengl; static int display_remote; const char* keyboard_layout = NULL; @@ -1990,6 +1991,15 @@ static DisplayType select_display(const char *p) } else { goto invalid_sdl_args; } + } else if (strstart(opts, ",gl=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "on", &nextopt)) { + request_opengl = 1; + } else if (strstart(opts, "off", &nextopt)) { + request_opengl = 0; + } else { + goto invalid_sdl_args; + } } else { invalid_sdl_args: fprintf(stderr, "Invalid SDL option string: %s\n", p); @@ -4005,6 +4015,19 @@ int main(int argc, char **argv, char **envp) early_gtk_display_init(); } #endif +#if defined(CONFIG_SDL) + if (display_type == DT_SDL) { + sdl_display_early_init(request_opengl); + } +#endif + if (request_opengl == 1 && display_opengl == 0) { +#if defined(CONFIG_OPENGL) + fprintf(stderr, "OpenGL is not supported by the display.\n"); +#else + fprintf(stderr, "QEMU was built without opengl support.\n"); +#endif + exit(1); + } socket_init(); |