wine/dlls/winewayland.drv/opengl.c

276 lines
9.3 KiB
C

/*
* Wayland OpenGL functions
*
* Copyright 2020 Alexandros Frantzis for Collabora Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "waylanddrv.h"
#include "wine/debug.h"
#ifdef HAVE_LIBWAYLAND_EGL
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
#include <wayland-egl.h>
#include "wine/opengl_driver.h"
static const struct egl_platform *egl;
static const struct opengl_funcs *funcs;
static const struct opengl_drawable_funcs wayland_drawable_funcs;
struct wayland_gl_drawable
{
struct opengl_drawable base;
struct wl_egl_window *wl_egl_window;
};
static struct wayland_gl_drawable *impl_from_opengl_drawable(struct opengl_drawable *base)
{
return CONTAINING_RECORD(base, struct wayland_gl_drawable, base);
}
static void wayland_drawable_destroy(struct opengl_drawable *base)
{
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
if (gl->wl_egl_window) wl_egl_window_destroy(gl->wl_egl_window);
}
static EGLConfig egl_config_for_format(int format)
{
assert(format > 0 && format <= 2 * egl->config_count);
if (format <= egl->config_count) return egl->configs[format - 1];
return egl->configs[format - egl->config_count - 1];
}
static void wayland_gl_drawable_sync_size(struct wayland_gl_drawable *gl)
{
int client_width, client_height;
RECT client_rect = {0};
NtUserGetClientRect(gl->base.client->hwnd, &client_rect, NtUserGetDpiForWindow(gl->base.client->hwnd));
client_width = client_rect.right - client_rect.left;
client_height = client_rect.bottom - client_rect.top;
if (client_width == 0 || client_height == 0) client_width = client_height = 1;
wl_egl_window_resize(gl->wl_egl_window, client_width, client_height, 0, 0);
}
static BOOL wayland_opengl_surface_create(HWND hwnd, HDC hdc, int format, struct opengl_drawable **drawable)
{
EGLConfig config = egl_config_for_format(format);
struct wayland_client_surface *client;
EGLint attribs[4], *attrib = attribs;
struct opengl_drawable *previous;
struct wayland_gl_drawable *gl;
RECT rect;
TRACE("hwnd=%p format=%d\n", hwnd, format);
if ((previous = *drawable) && previous->format == format) return TRUE;
NtUserGetClientRect(hwnd, &rect, NtUserGetDpiForWindow(hwnd));
if (rect.right == rect.left) rect.right = rect.left + 1;
if (rect.bottom == rect.top) rect.bottom = rect.top + 1;
if (!egl->has_EGL_EXT_present_opaque)
WARN("Missing EGL_EXT_present_opaque extension\n");
else
{
*attrib++ = EGL_PRESENT_OPAQUE_EXT;
*attrib++ = EGL_TRUE;
}
*attrib++ = EGL_NONE;
if (!(client = wayland_client_surface_create(hwnd))) return FALSE;
gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, &client->client);
client_surface_release(&client->client);
if (!gl) return FALSE;
if (!(gl->wl_egl_window = wl_egl_window_create(client->wl_surface, rect.right, rect.bottom))) goto err;
if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->wl_egl_window, attribs))) goto err;
set_client_surface(hwnd, client);
TRACE("Created drawable %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface);
if (previous) opengl_drawable_release( previous );
*drawable = &gl->base;
return TRUE;
err:
opengl_drawable_release(&gl->base);
return FALSE;
}
static void wayland_init_egl_platform(struct egl_platform *platform)
{
platform->type = EGL_PLATFORM_WAYLAND_KHR;
platform->native_display = process_wayland.wl_display;
platform->force_pbuffer_formats = TRUE;
egl = platform;
}
static void wayland_drawable_flush(struct opengl_drawable *base, UINT flags)
{
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
TRACE("drawable %s, flags %#x\n", debugstr_opengl_drawable(base), flags);
if (flags & GL_FLUSH_INTERVAL) funcs->p_eglSwapInterval(egl->display, abs(base->interval));
/* Since context_flush is called from operations that may latch the native size,
* perform any pending resizes before calling them. */
if (flags & GL_FLUSH_UPDATED) wayland_gl_drawable_sync_size(gl);
}
static BOOL wayland_drawable_swap(struct opengl_drawable *base)
{
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
client_surface_present(base->client, NULL);
funcs->p_eglSwapBuffers(egl->display, gl->base.surface);
return TRUE;
}
struct wayland_pbuffer
{
struct opengl_drawable base;
struct wl_surface *surface;
struct wl_egl_window *window;
};
static struct wayland_pbuffer *pbuffer_from_opengl_drawable(struct opengl_drawable *base)
{
return CONTAINING_RECORD(base, struct wayland_pbuffer, base);
}
static void wayland_pbuffer_destroy(struct opengl_drawable *base)
{
struct wayland_pbuffer *gl = pbuffer_from_opengl_drawable(base);
TRACE("%s\n", debugstr_opengl_drawable(base));
if (gl->window)
wl_egl_window_destroy(gl->window);
if (gl->surface)
wl_surface_destroy(gl->surface);
}
static const struct opengl_drawable_funcs wayland_pbuffer_funcs =
{
.destroy = wayland_pbuffer_destroy,
};
static BOOL wayland_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target,
GLint max_level, GLsizei *width, GLsizei *height, struct opengl_drawable **surface)
{
EGLConfig config = egl_config_for_format(format);
struct wayland_pbuffer *gl;
TRACE("hdc %p, format %d, largest %u, texture_format %#x, texture_target %#x, max_level %#x, width %d, height %d, private %p\n",
hdc, format, largest, texture_format, texture_target, max_level, *width, *height, surface);
if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_pbuffer_funcs, format, NULL))) return FALSE;
/* Wayland EGL doesn't support pixmap or pbuffer, create a dummy window surface to act as the target render surface. */
if (!(gl->surface = wl_compositor_create_surface(process_wayland.wl_compositor))) goto err;
if (!(gl->window = wl_egl_window_create(gl->surface, *width, *height))) goto err;
if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->window, NULL))) goto err;
TRACE("Created pbuffer %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface);
*surface = &gl->base;
return TRUE;
err:
opengl_drawable_release(&gl->base);
return FALSE;
}
static BOOL wayland_pbuffer_updated(HDC hdc, struct opengl_drawable *base, GLenum cube_face, GLint mipmap_level)
{
return GL_TRUE;
}
static UINT wayland_pbuffer_bind(HDC hdc, struct opengl_drawable *base, GLenum buffer)
{
return -1; /* use default implementation */
}
static struct opengl_driver_funcs wayland_driver_funcs =
{
.p_init_egl_platform = wayland_init_egl_platform,
.p_surface_create = wayland_opengl_surface_create,
.p_pbuffer_create = wayland_pbuffer_create,
.p_pbuffer_updated = wayland_pbuffer_updated,
.p_pbuffer_bind = wayland_pbuffer_bind,
};
static const struct opengl_drawable_funcs wayland_drawable_funcs =
{
.destroy = wayland_drawable_destroy,
.flush = wayland_drawable_flush,
.swap = wayland_drawable_swap,
};
/**********************************************************************
* WAYLAND_OpenGLInit
*/
UINT WAYLAND_OpenGLInit(UINT version, const struct opengl_funcs *opengl_funcs, const struct opengl_driver_funcs **driver_funcs)
{
if (version != WINE_OPENGL_DRIVER_VERSION)
{
ERR("Version mismatch, opengl32 wants %u but driver has %u\n",
version, WINE_OPENGL_DRIVER_VERSION);
return STATUS_INVALID_PARAMETER;
}
if (!opengl_funcs->egl_handle) return STATUS_NOT_SUPPORTED;
funcs = opengl_funcs;
wayland_driver_funcs.p_get_proc_address = (*driver_funcs)->p_get_proc_address;
wayland_driver_funcs.p_init_pixel_formats = (*driver_funcs)->p_init_pixel_formats;
wayland_driver_funcs.p_describe_pixel_format = (*driver_funcs)->p_describe_pixel_format;
wayland_driver_funcs.p_init_wgl_extensions = (*driver_funcs)->p_init_wgl_extensions;
wayland_driver_funcs.p_context_create = (*driver_funcs)->p_context_create;
wayland_driver_funcs.p_context_destroy = (*driver_funcs)->p_context_destroy;
wayland_driver_funcs.p_make_current = (*driver_funcs)->p_make_current;
*driver_funcs = &wayland_driver_funcs;
return STATUS_SUCCESS;
}
#else /* No GL */
UINT WAYLAND_OpenGLInit(UINT version, const struct opengl_funcs *opengl_funcs, const struct opengl_driver_funcs **driver_funcs)
{
return STATUS_NOT_IMPLEMENTED;
}
#endif