From bdcc5be476b3f6e34a47734e46d2ef5bd4a82048 Mon Sep 17 00:00:00 2001
From: Ryan Pavlik <ryan.pavlik@collabora.com>
Date: Fri, 15 Oct 2021 14:36:57 -0500
Subject: [PATCH] c/client: Win32 OpenGL client compositor

Co-authored-by: Milan Jaros <milan.jaros@vsb.cz>
Co-authored-by: Jakob Bornecrantz <jakob@collabora.com>
---
 src/xrt/compositor/CMakeLists.txt             |   6 +
 .../compositor/client/comp_gl_win32_client.c  | 242 ++++++++++++++++++
 .../compositor/client/comp_gl_win32_client.h  |  64 +++++
 .../compositor/client/comp_gl_win32_glue.c    |  25 ++
 4 files changed, 337 insertions(+)
 create mode 100644 src/xrt/compositor/client/comp_gl_win32_client.c
 create mode 100644 src/xrt/compositor/client/comp_gl_win32_client.h
 create mode 100644 src/xrt/compositor/client/comp_gl_win32_glue.c

diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt
index d40b5edda..46faef489 100644
--- a/src/xrt/compositor/CMakeLists.txt
+++ b/src/xrt/compositor/CMakeLists.txt
@@ -54,6 +54,12 @@ if(XRT_HAVE_OPENGL_GLX AND XRT_HAVE_XLIB)
 	target_link_libraries(comp_client PRIVATE OpenGL::GLX)
 endif()
 
+if(XRT_HAVE_OPENGL AND WIN32)
+	target_sources(
+		comp_client PRIVATE client/comp_gl_win32_client.c client/comp_gl_win32_client.h
+				    client/comp_gl_win32_glue.c
+		)
+endif()
 if(XRT_HAVE_EGL)
 	target_sources(comp_client PRIVATE client/comp_egl_client.c client/comp_egl_client.h)
 endif()
diff --git a/src/xrt/compositor/client/comp_gl_win32_client.c b/src/xrt/compositor/client/comp_gl_win32_client.c
new file mode 100644
index 000000000..2071e6af7
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_client.c
@@ -0,0 +1,242 @@
+// Copyright 2019-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Win32 client side glue to compositor implementation.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Milan Jaros <milan.jaros@vsb.cz>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup comp_client
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util/u_misc.h"
+#include "util/u_logging.h"
+
+#include "xrt/xrt_gfx_win32.h"
+
+#include "client/comp_gl_win32_client.h"
+#include "client/comp_gl_memobj_swapchain.h"
+
+#include "ogl/ogl_api.h"
+#include "ogl/wgl_api.h"
+
+/*
+ *
+ * OpenGL context helper.
+ *
+ */
+
+static inline bool
+context_matches(const struct client_gl_context *a, const struct client_gl_context *b)
+{
+	return a->hDC == b->hDC && a->hGLRC == b->hGLRC;
+}
+
+static inline void
+context_save_current(struct client_gl_context *current_ctx)
+{
+	current_ctx->hDC = wglGetCurrentDC();
+	current_ctx->hGLRC = wglGetCurrentContext();
+}
+
+static inline bool
+context_make_current(const struct client_gl_context *ctx_to_make_current)
+{
+	if (wglMakeCurrent(ctx_to_make_current->hDC, ctx_to_make_current->hGLRC)) {
+		return true;
+	}
+	return false;
+}
+
+/*!
+ * Down-cast helper.
+ *
+ * @private @memberof client_gl_win32_compositor
+ */
+static inline struct client_gl_win32_compositor *
+client_gl_win32_compositor(struct xrt_compositor *xc)
+{
+	return (struct client_gl_win32_compositor *)xc;
+}
+
+static void
+client_gl_win32_compositor_destroy(struct xrt_compositor *xc)
+{
+	struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
+
+	client_gl_compositor_close(&c->base);
+
+	FreeLibrary(c->opengl);
+	c->opengl = NULL;
+
+	free(c);
+}
+
+static xrt_result_t
+client_gl_context_begin(struct xrt_compositor *xc)
+{
+	struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
+
+	struct client_gl_context *app_ctx = &c->app_context;
+
+	os_mutex_lock(&c->base.context_mutex);
+
+	context_save_current(&c->temp_context);
+
+	bool need_make_current = !context_matches(&c->temp_context, app_ctx);
+
+	U_LOG_T("GL Context begin: need makeCurrent: %d (current %p -> app %p)", need_make_current,
+	        (void *)c->temp_context.hGLRC, (void *)app_ctx->hGLRC);
+
+	if (need_make_current && !context_make_current(app_ctx)) {
+		os_mutex_unlock(&c->base.context_mutex);
+
+		U_LOG_E("Failed to make WGL context current");
+		// No need to restore on failure.
+		return XRT_ERROR_OPENGL;
+	}
+
+	return XRT_SUCCESS;
+}
+
+static void
+client_gl_context_end(struct xrt_compositor *xc)
+{
+	struct client_gl_win32_compositor *c = client_gl_win32_compositor(xc);
+
+	struct client_gl_context *app_ctx = &c->app_context;
+
+	struct client_gl_context *current_wgl_context = &c->temp_context;
+
+	bool need_make_current = !context_matches(&c->temp_context, app_ctx);
+
+	U_LOG_T("GL Context end: need makeCurrent: %d (app %p -> current %p)", need_make_current,
+	        (void *)app_ctx->hGLRC, (void *)c->temp_context.hGLRC);
+
+	if (need_make_current && !context_make_current(current_wgl_context)) {
+		U_LOG_E("Failed to make old WGL context current!");
+		// fall through to os_mutex_unlock even if we didn't succeed in restoring the context
+	}
+
+	os_mutex_unlock(&c->base.context_mutex);
+}
+
+static GLADapiproc
+client_gl_get_proc_addr(void *userptr, const char *name)
+{
+	GLADapiproc ret = (GLADapiproc)wglGetProcAddress(name);
+	if (ret == NULL) {
+		ret = (GLADapiproc)GetProcAddress((HMODULE)userptr, name);
+	}
+	return ret;
+}
+
+struct client_gl_win32_compositor *
+client_gl_win32_compositor_create(struct xrt_compositor_native *xcn, void *hDC, void *hGLRC)
+{
+	// Save old GLX context.
+	struct client_gl_context current_ctx;
+	context_save_current(&current_ctx);
+
+	// The context and drawables given from the app.
+	struct client_gl_context app_ctx = {
+	    .hDC = hDC,
+	    .hGLRC = hGLRC,
+	};
+
+
+	/*
+	 * Make given context current if needed.
+	 */
+
+	bool need_make_current = !context_matches(&current_ctx, &app_ctx);
+
+	if (need_make_current && !context_make_current(&app_ctx)) {
+		U_LOG_E("Failed to make WGL context current");
+		// No need to restore on failure.
+		return NULL;
+	}
+
+
+	/*
+	 * Load functions.
+	 */
+
+	HMODULE opengl = LoadLibraryW(L"opengl32.dll");
+
+	int wgl_result = gladLoadWGLUserPtr(hDC, client_gl_get_proc_addr, opengl);
+	int gl_result = gladLoadGLUserPtr(client_gl_get_proc_addr, opengl);
+
+	if (glGetString != NULL) {
+		U_LOG_D(                      //
+		    "OpenGL context:"         //
+		    "\n\tGL_VERSION: %s"      //
+		    "\n\tGL_RENDERER: %s"     //
+		    "\n\tGL_VENDOR: %s",      //
+		    glGetString(GL_VERSION),  //
+		    glGetString(GL_RENDERER), //
+		    glGetString(GL_VENDOR));  //
+	}
+
+
+	/*
+	 * Return to app context.
+	 */
+
+	if (need_make_current && !context_make_current(&current_ctx)) {
+		U_LOG_E("Failed to make old WGL context current!");
+	}
+
+
+	/*
+	 * Checking of context.
+	 */
+
+	// Only do error checking here.
+	if (wgl_result == 0 || gl_result == 0) {
+		U_LOG_E("Failed to load GLAD functions gladLoadWGL: 0x%08x, gladLoadGL: 0x%08x", wgl_result, gl_result);
+		FreeLibrary(opengl);
+		return NULL;
+	}
+
+#define CHECK_REQUIRED_EXTENSION(EXT)                                                                                  \
+	do {                                                                                                           \
+		if (!GLAD_GL_##EXT) {                                                                                  \
+			U_LOG_E("%s - Required OpenGL extension GL_" #EXT " not available", __func__);                 \
+			FreeLibrary(opengl);                                                                           \
+			return NULL;                                                                                   \
+		}                                                                                                      \
+	} while (0)
+
+	CHECK_REQUIRED_EXTENSION(EXT_memory_object); // why is this failing? the gpuinfo.org tool says I have it.
+	CHECK_REQUIRED_EXTENSION(EXT_memory_object_win32);
+
+#undef CHECK_REQUIRED_EXTENSION
+
+
+	/*
+	 * Checking complete, create client compositor here.
+	 */
+
+	struct client_gl_win32_compositor *c = U_TYPED_CALLOC(struct client_gl_win32_compositor);
+
+	// Move the app context to the struct.
+	c->app_context = app_ctx;
+	// Same for the opengl library handle
+	c->opengl = opengl;
+
+	if (!client_gl_compositor_init(&c->base, xcn, client_gl_context_begin, client_gl_context_end,
+	                               client_gl_memobj_swapchain_create, NULL)) {
+		U_LOG_E("Failed to init parent GL client compositor!");
+		FreeLibrary(opengl);
+		free(c);
+		return NULL;
+	}
+
+	c->base.base.base.destroy = client_gl_win32_compositor_destroy;
+
+	return c;
+}
diff --git a/src/xrt/compositor/client/comp_gl_win32_client.h b/src/xrt/compositor/client/comp_gl_win32_client.h
new file mode 100644
index 000000000..67ae8f1d7
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_client.h
@@ -0,0 +1,64 @@
+// Copyright 2019-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  OpenGL on Win32 client side glue to compositor header.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup comp_client
+ */
+
+#pragma once
+
+#include "xrt/xrt_gfx_win32.h"
+#include "client/comp_gl_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct client_gl_context
+{
+	HDC hDC;
+	HGLRC hGLRC;
+};
+
+/*!
+ * @class client_gl_win32_compositor
+ * A client facing win32 OpenGL base compositor.
+ *
+ * @ingroup comp_client
+ * @extends client_gl_compositor
+ */
+struct client_gl_win32_compositor
+{
+	//! OpenGL compositor wrapper base.
+	struct client_gl_compositor base;
+
+	/*!
+	 * Temporary storage for "current" OpenGL context while app_context is
+	 * made current using context_begin/context_end. We only need one because
+	 * app_context can only be made current in one thread at a time too.
+	 */
+	struct client_gl_context temp_context;
+
+	//! GL context provided in graphics binding.
+	struct client_gl_context app_context;
+
+	//! The OpenGL library
+	HMODULE opengl;
+};
+
+/*!
+ * Create a new client_gl_win32_compositor.
+ *
+ * @public @memberof client_gl_win32_compositor
+ * @see xrt_compositor_native
+ */
+struct client_gl_win32_compositor *
+client_gl_win32_compositor_create(struct xrt_compositor_native *xcn, void *hDC, void *hGLRC);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/xrt/compositor/client/comp_gl_win32_glue.c b/src/xrt/compositor/client/comp_gl_win32_glue.c
new file mode 100644
index 000000000..40907e804
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_glue.c
@@ -0,0 +1,25 @@
+// Copyright 2019-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Glue code to OpenGL Win32 client side code.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup comp_client
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "xrt/xrt_gfx_win32.h"
+
+#include "client/comp_gl_win32_client.h"
+
+
+struct xrt_compositor_gl *
+xrt_gfx_provider_create_gl_win32(struct xrt_compositor_native *xcn, void *hDC, void *hGLRC)
+{
+	struct client_gl_win32_compositor *xcc = client_gl_win32_compositor_create(xcn, hDC, hGLRC);
+
+	return &xcc->base.base;
+}