xref: /openbmc/qemu/ui/console-gl.c (revision 1c37425423f60c3e0deec6e942cfb17982fd8b6d)
1 /*
2  * QEMU graphical console -- opengl helper bits
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 #include "qemu/osdep.h"
28 #include "qemu/error-report.h"
29 #include "ui/console.h"
30 #include "ui/shader.h"
31 
32 /* ---------------------------------------------------------------------- */
33 
console_gl_check_format(DisplayChangeListener * dcl,pixman_format_code_t format)34 bool console_gl_check_format(DisplayChangeListener *dcl,
35                              pixman_format_code_t format)
36 {
37     switch (format) {
38     case PIXMAN_BE_b8g8r8x8:
39     case PIXMAN_BE_b8g8r8a8:
40     case PIXMAN_r5g6b5:
41         return true;
42     default:
43         return false;
44     }
45 }
46 
surface_gl_create_texture(QemuGLShader * gls,DisplaySurface * surface)47 void surface_gl_create_texture(QemuGLShader *gls,
48                                DisplaySurface *surface)
49 {
50     assert(gls);
51     assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface)));
52 
53     if (surface->texture) {
54         return;
55     }
56 
57     switch (surface_format(surface)) {
58     case PIXMAN_BE_b8g8r8x8:
59     case PIXMAN_BE_b8g8r8a8:
60         surface->glformat = GL_BGRA_EXT;
61         surface->gltype = GL_UNSIGNED_BYTE;
62         break;
63     case PIXMAN_BE_x8r8g8b8:
64     case PIXMAN_BE_a8r8g8b8:
65         surface->glformat = GL_RGBA;
66         surface->gltype = GL_UNSIGNED_BYTE;
67         break;
68     case PIXMAN_r5g6b5:
69         surface->glformat = GL_RGB;
70         surface->gltype = GL_UNSIGNED_SHORT_5_6_5;
71         break;
72     default:
73         g_assert_not_reached();
74     }
75 
76     glGenTextures(1, &surface->texture);
77     glEnable(GL_TEXTURE_2D);
78     glBindTexture(GL_TEXTURE_2D, surface->texture);
79     glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT,
80                   surface_stride(surface) / surface_bytes_per_pixel(surface));
81     if (epoxy_is_desktop_gl()) {
82         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
83                      surface_width(surface),
84                      surface_height(surface),
85                      0, surface->glformat, surface->gltype,
86                      surface_data(surface));
87     } else {
88         glTexImage2D(GL_TEXTURE_2D, 0, surface->glformat,
89                      surface_width(surface),
90                      surface_height(surface),
91                      0, surface->glformat, surface->gltype,
92                      surface_data(surface));
93         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
94     }
95 
96     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
97     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
98 }
99 
surface_gl_create_texture_from_fd(DisplaySurface * surface,int fd,GLuint * texture,GLuint * mem_obj)100 bool surface_gl_create_texture_from_fd(DisplaySurface *surface,
101                                        int fd, GLuint *texture,
102                                        GLuint *mem_obj)
103 {
104     unsigned long size = surface_stride(surface) * surface_height(surface);
105     GLenum err = glGetError();
106     *texture = 0;
107     *mem_obj = 0;
108 
109     if (!epoxy_has_gl_extension("GL_EXT_memory_object") ||
110         !epoxy_has_gl_extension("GL_EXT_memory_object_fd")) {
111         error_report("spice: required OpenGL extensions not supported: "
112                      "GL_EXT_memory_object and GL_EXT_memory_object_fd");
113         return false;
114     }
115 
116 #ifdef GL_EXT_memory_object_fd
117     glCreateMemoryObjectsEXT(1, mem_obj);
118     glImportMemoryFdEXT(*mem_obj, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd);
119 
120     err = glGetError();
121     if (err != GL_NO_ERROR) {
122         error_report("spice: cannot import memory object from fd");
123         goto cleanup_mem;
124     }
125 
126     glGenTextures(1, texture);
127     glBindTexture(GL_TEXTURE_2D, *texture);
128     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT);
129     glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, surface_width(surface),
130                          surface_height(surface), *mem_obj, 0);
131     err = glGetError();
132     if (err != GL_NO_ERROR) {
133         error_report("spice: cannot create texture from memory object");
134         goto cleanup_tex_and_mem;
135     }
136     return true;
137 
138 cleanup_tex_and_mem:
139     glDeleteTextures(1, texture);
140 cleanup_mem:
141     glDeleteMemoryObjectsEXT(1, mem_obj);
142 
143 #endif
144     return false;
145 }
146 
surface_gl_update_texture(QemuGLShader * gls,DisplaySurface * surface,int x,int y,int w,int h)147 void surface_gl_update_texture(QemuGLShader *gls,
148                                DisplaySurface *surface,
149                                int x, int y, int w, int h)
150 {
151     uint8_t *data = (void *)surface_data(surface);
152 
153     assert(gls);
154 
155     if (surface->texture) {
156         glBindTexture(GL_TEXTURE_2D, surface->texture);
157         glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT,
158                       surface_stride(surface)
159                       / surface_bytes_per_pixel(surface));
160         glTexSubImage2D(GL_TEXTURE_2D, 0,
161                         x, y, w, h,
162                         surface->glformat, surface->gltype,
163                         data + surface_stride(surface) * y
164                         + surface_bytes_per_pixel(surface) * x);
165     }
166 }
167 
surface_gl_render_texture(QemuGLShader * gls,DisplaySurface * surface)168 void surface_gl_render_texture(QemuGLShader *gls,
169                                DisplaySurface *surface)
170 {
171     assert(gls);
172 
173     glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
174     glClear(GL_COLOR_BUFFER_BIT);
175 
176     qemu_gl_run_texture_blit(gls, false);
177 }
178 
surface_gl_destroy_texture(QemuGLShader * gls,DisplaySurface * surface)179 void surface_gl_destroy_texture(QemuGLShader *gls,
180                                 DisplaySurface *surface)
181 {
182     if (!surface || !surface->texture) {
183         return;
184     }
185     glDeleteTextures(1, &surface->texture);
186     surface->texture = 0;
187 #ifdef GL_EXT_memory_object_fd
188     if (surface->mem_obj) {
189         glDeleteMemoryObjectsEXT(1, &surface->mem_obj);
190         surface->mem_obj = 0;
191     }
192 #endif
193 }
194 
surface_gl_setup_viewport(QemuGLShader * gls,DisplaySurface * surface,int ww,int wh)195 void surface_gl_setup_viewport(QemuGLShader *gls,
196                                DisplaySurface *surface,
197                                int ww, int wh)
198 {
199     int gw, gh, stripe;
200     float sw, sh;
201 
202     assert(gls);
203 
204     gw = surface_width(surface);
205     gh = surface_height(surface);
206 
207     sw = (float)ww/gw;
208     sh = (float)wh/gh;
209     if (sw < sh) {
210         stripe = wh - wh*sw/sh;
211         glViewport(0, stripe / 2, ww, wh - stripe);
212     } else {
213         stripe = ww - ww*sh/sw;
214         glViewport(stripe / 2, 0, ww - stripe, wh);
215     }
216 }
217