DisplaySurface *surface;
int idx;
+
+#ifdef CONFIG_OPENGL
+ uint64_t gen;
+ bool updated;
+ ConsoleGLState *gls;
+ QEMUGLContext ctx;
+ bool is_scanout;
+ uint32_t scanout_fbs[2];
+ bool backing_y_0_top;
+ uint32_t backing_width, backing_height;
+#endif
} *qt5_console;
void qt5_graphic_hw_invalidate(void)
.dpy_cursor_define = qt5_mouse_define,
};
+#ifdef CONFIG_OPENGL
+static void qt5_gl_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ struct qt5_state *con = container_of(dcl, struct qt5_state, dcl);
+
+ if (con->is_scanout)
+ return;
+
+ if (con->ctx)
+ qt5_gl_make_context_current_internal(con->ctx);
+ surface_gl_update_texture(con->gls, con->surface, x, y, w, h);
+ con->updated = true;
+
+ /* Note that, unlike non-GL, we do not call composite_brightness_image.
+ * In this case, brightness rendering is the responsibility of the display. */
+}
+
+static void qt5_gl_switch(DisplayChangeListener *dcl,
+ DisplaySurface *new_surface)
+{
+ struct qt5_state *con = container_of(dcl, struct qt5_state, dcl);
+
+ if (con->ctx)
+ qt5_gl_make_context_current_internal(con->ctx);
+
+ surface_gl_destroy_texture(NULL, con->surface);
+
+ if (new_surface && !con->gls) {
+ con->gls = console_gl_init_context();
+ } else if (!new_surface && con->gls) {
+ console_gl_fini_context(con->gls);
+ con->gls = NULL;
+ return;
+ } else if (con->surface &&
+ (surface_width(con->surface) != surface_width(new_surface) ||
+ surface_height(con->surface) != surface_height(new_surface))) {
+ // TODO: we should adjust display size.
+ // We should warn to user since we can not adjust display size now.
+ LOG_WARNING("display size is changed.\n");
+ }
+
+ con->surface = new_surface;
+ con->gen += 1;
+ // TODO: con->is_scanout?
+ surface_gl_create_texture(con->gls, con->surface);
+}
+
+static void qt5_gl_refresh(DisplayChangeListener *dcl)
+{
+ struct qt5_state *con = container_of(dcl, struct qt5_state, dcl);
+
+ if (con->ctx)
+ qt5_gl_make_context_current_internal(con->ctx);
+
+ graphic_hw_update(dcl->con);
+ qt5_refresh_internal();
+ if (con->surface && !con->is_scanout && con->updated) {
+ bool should_free;
+ con->surface->texture = qt5_gl_refresh_internal(con->surface->texture, surface_width(con->surface), surface_height(con->surface), con->gen, &should_free);
+ if (should_free) {
+ surface_gl_destroy_texture(con->gls, con->surface);
+ }
+ if (con->surface->texture == 0) {
+ surface_gl_create_texture(con->gls, con->surface);
+ }
+ con->updated = false;
+ }
+}
+
+static QEMUGLContext qt5_gl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ return qt5_gl_create_context_internal(params->major_ver, params->minor_ver);
+}
+
+static void qt5_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ qt5_gl_destroy_context_internal(ctx);
+}
+
+static int qt5_gl_make_context_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ return qt5_gl_make_context_current_internal(ctx);
+}
+
+static QEMUGLContext qt5_gl_get_current_context(DisplayChangeListener *dcl)
+{
+ return qt5_gl_get_current_context_internal();
+}
+
+static void qt5_gl_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t backing_width, uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ struct qt5_state *con = container_of(dcl, struct qt5_state, dcl);
+
+ if (backing_id == 0) {
+ con->is_scanout = false;
+ return;
+ }
+ con->is_scanout = true;
+
+ if (con->ctx)
+ qt5_gl_make_context_current_internal(con->ctx);
+
+ con->backing_y_0_top = backing_y_0_top;
+ con->backing_width = backing_width;
+ con->backing_height = backing_height;
+
+ if (con->scanout_fbs[0] == 0) {
+ // TODO: these leak?
+ glGenFramebuffers(2, con->scanout_fbs);
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, con->scanout_fbs[0]);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, con->scanout_fbs[1]);
+ glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, backing_id, 0);
+
+}
+
+static void qt5_gl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ struct qt5_state *con = container_of(dcl, struct qt5_state, dcl);
+
+ if (con->ctx)
+ qt5_gl_make_context_current_internal(con->ctx);
+
+ glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, con->surface->texture, 0);
+
+ uint32_t y1 = con->backing_y_0_top ? con->backing_height : 0;
+ uint32_t y2 = con->backing_y_0_top ? 0 : con->backing_height;
+ glBlitFramebuffer(0, y1, con->backing_width, y2,
+ 0, 0, surface_width(con->surface), surface_height(con->surface),
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ bool should_free;
+ con->surface->texture = qt5_gl_refresh_internal(con->surface->texture, surface_width(con->surface), surface_height(con->surface), con->gen, &should_free);
+ if (should_free) {
+ surface_gl_destroy_texture(con->gls, con->surface);
+ }
+ if (con->surface->texture == 0) {
+ surface_gl_create_texture(con->gls, con->surface);
+ }
+}
+
+static const DisplayChangeListenerOps dcl_gl_ops = {
+ .dpy_name = "qt5-gl",
+ .dpy_gfx_update = qt5_gl_update,
+ .dpy_gfx_switch = qt5_gl_switch,
+ .dpy_gfx_check_format = console_gl_check_format,
+ .dpy_refresh = qt5_gl_refresh,
+ .dpy_mouse_set = qt5_mouse_warp,
+ .dpy_cursor_define = qt5_mouse_define,
+
+ .dpy_gl_ctx_create = qt5_gl_create_context,
+ .dpy_gl_ctx_destroy = qt5_gl_destroy_context,
+ .dpy_gl_ctx_make_current = qt5_gl_make_context_current,
+ .dpy_gl_ctx_get_current = qt5_gl_get_current_context,
+ .dpy_gl_scanout = qt5_gl_scanout,
+ .dpy_gl_update = qt5_gl_scanout_flush,
+};
+#endif
+
void maru_early_qt5_display_init(enum maru_display_type display_type)
{
#ifdef CONFIG_DARWIN
case MARU_DISPLAY_TYPE_OFFSCREEN:
LOG_INFO("Display Type: QT5 Offscreen\n");
break;
+#ifdef CONFIG_OPENGL
+ case MARU_DISPLAY_TYPE_GL:
+ LOG_INFO("Display Type: QT5 GL\n");
+ break;
+#endif
}
}
}
qt5_num_outputs = i;
qt5_console = g_new0(struct qt5_state, qt5_num_outputs);
+#ifdef CONFIG_OPENGL
+ qt5_gl_init_items();
+#endif
for (i = 0; i < qt5_num_outputs; i++) {
QemuConsole *con = qemu_console_lookup_by_index(i);
+#ifdef CONFIG_OPENGL
+ qt5_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_ops;
+ qt5_console[i].gen = 0;
+ qt5_console[i].updated = false;
+ qt5_console[i].gls = NULL;
+ qt5_console[i].ctx = display_opengl ? qt5_gl_create_context_internal(3, 3) : NULL;
+ qt5_console[i].is_scanout = false;
+ qt5_console[i].scanout_fbs[0] = 0;
+#else
qt5_console[i].dcl.ops = &dcl_ops;
+#endif
qt5_console[i].dcl.con = con;
register_displaychangelistener(&qt5_console[i].dcl);
qt5_console[i].idx = i;
*
*/
+#include <memory>
+
#include <QApplication>
#include <QOpenGLContext>
#include "qt5_supplement.h"
+#include "displayglwidget.h"
#include "propertykeyword.h"
#include "mainwindow.h"
#include "layout/hardwarekey.h"
#include "resource/ui_strings.h"
#include "displaybase.h"
+// TODO: Figure out the compilation
+extern int display_opengl;
+
#include "util/new_debug_ch.h"
// XXX: all modules in ui/* are controlled by channel name "qt5_ui"
DECLARE_DEBUG_CHANNEL(qt5_ui);
void qt5_early_prepare(enum maru_display_type _display_type)
{
display_type = _display_type;
+#ifdef CONFIG_OPENGL
+ display_opengl = display_type == MARU_DISPLAY_TYPE_GL;
+#endif
Q_INIT_RESOURCE(resource);
- if (display_type == MARU_DISPLAY_TYPE_ONSCREEN) {
+ if (display_type == MARU_DISPLAY_TYPE_ONSCREEN || display_type == MARU_DISPLAY_TYPE_GL) {
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
mainwindow->updateTexture(dpy_item);
}
}
+
+#ifdef CONFIG_OPENGL
+#define DPY_ITEM_NO 5
+static dpy_item dpy_item_pool[DPY_ITEM_NO];
+static uint64_t dpy_item_gen[DPY_ITEM_NO];
+
+void qt5_gl_init_items()
+{
+ for (int i = 0; i < DPY_ITEM_NO; ++i) {
+ qemu_mutex_init(&dpy_item_pool[i].mutex);
+ dpy_item_pool[i].available = true;
+ }
+}
+// TODO: Is there a good place to put this?
+/* void qt5_gl_destroy_items()
+{
+ for (int i = 0; i < DPY_ITEM_NO; ++i) {
+ qemu_mutex_destroy(&dpy_item_pool[i].mutex);
+ }
+} */
+
+uint32_t qt5_gl_refresh_internal(uint32_t tex, uint32_t width, uint32_t height, uint64_t gen, bool *should_free)
+{
+ uint32_t ret = tex;
+ *should_free = false;
+
+ if (mainwindow) {
+ int item_id = 0;
+ while (true)
+ {
+ auto item = &dpy_item_pool[item_id];
+ qemu_mutex_lock(&item->mutex);
+ bool ok = item->available;
+ if (ok) {
+ ret = item->tex;
+ if (dpy_item_gen[item_id] != gen || /* HACK HACK HACK */ item->tex < 25) {
+ /* This tells the caller that the texture should not be used anymore.
+ * Preferably we could handle this right here, but the caller can do it
+ * conveniently and I couldn't get compilation to work. */
+ dpy_item_gen[item_id] = gen;
+ *should_free = true;
+ }
+ item->tex = tex;
+ item->width = width;
+ item->height = height;
+ item->available = false;
+ }
+ qemu_mutex_unlock(&item->mutex);
+ if (ok) {
+ mainwindow->updateTexture(item);
+ break;
+ }
+ item_id++;
+ if (item_id == DPY_ITEM_NO) {
+ break;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+void *qt5_gl_create_context_internal(int major, int minor)
+{
+ if (mainwindow) {
+ return ((DisplayGLWidget *)mainwindow->getDisplay())->createSharedContext(major, minor);
+ } else {
+ return nullptr;
+ }
+}
+
+void qt5_gl_destroy_context_internal(void *_ctx)
+{
+ auto ctx = (QOpenGLContext *)_ctx;
+ delete ctx;
+}
+
+int qt5_gl_make_context_current_internal(void *_ctx)
+{
+ auto ctx = (QOpenGLContext *)_ctx;
+ if (mainwindow) {
+ return ((DisplayGLWidget *)mainwindow->getDisplay())->activateSharedContext(ctx) ? 0 : -1;
+ } else {
+ return -1;
+ }
+}
+
+void *qt5_gl_get_current_context_internal()
+{
+ return QOpenGLContext::currentContext();
+}
+
+void qt5_gl_display_force_redraw()
+{
+ if (mainwindow) {
+ ((DisplayGLWidget *)mainwindow->getDisplay())->update();
+ }
+}
+#endif