diff --git a/src/xrt/auxiliary/CMakeLists.txt b/src/xrt/auxiliary/CMakeLists.txt
index 8ce58be57034135162eeb0ffad136b44f1546c81..8fdadfbba2e3f6daaee0057f47f57a6fe7242d2b 100644
--- a/src/xrt/auxiliary/CMakeLists.txt
+++ b/src/xrt/auxiliary/CMakeLists.txt
@@ -27,6 +27,9 @@ if(XRT_HAVE_OPENGL OR XRT_HAVE_OPENGLES)
 	if(XRT_HAVE_EGL)
 		target_sources(aux_ogl PRIVATE ogl/egl_api.c ogl/egl_api.h)
 	endif()
+	if(XRT_HAVE_OPENGL AND WIN32)
+		target_sources(aux_ogl PRIVATE ogl/wgl_api.c ogl/wgl_api.h)
+	endif()	
 	if(XRT_HAVE_OPENGLES)
 		target_link_libraries(aux_ogl PUBLIC EGL::EGL)
 	endif()
diff --git a/src/xrt/auxiliary/ogl/wgl_api.c b/src/xrt/auxiliary/ogl/wgl_api.c
new file mode 100644
index 0000000000000000000000000000000000000000..33a1073c6172644659410b4e906f913db81273a1
--- /dev/null
+++ b/src/xrt/auxiliary/ogl/wgl_api.c
@@ -0,0 +1,10 @@
+// Copyright 2022, James Hogan <james@albanarts.com>
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  WGL API wrapper.
+ * @author James Hogan <james@albanarts.com>
+ * @ingroup aux_ogl
+ */
+
+#include "../../external/glad/src/wgl.c"
diff --git a/src/xrt/auxiliary/ogl/wgl_api.h b/src/xrt/auxiliary/ogl/wgl_api.h
new file mode 100644
index 0000000000000000000000000000000000000000..bfff2cfc05d341878109676d92e3d1d40505a3b7
--- /dev/null
+++ b/src/xrt/auxiliary/ogl/wgl_api.h
@@ -0,0 +1,17 @@
+// Copyright 2022, James Hogan <james@albanarts.com>
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  GLX API wrapper header.
+ * @author James Hogan <james@albanarts.com>
+ * @ingroup aux_ogl
+ */
+
+#pragma once
+
+#include "xrt/xrt_gfx_win32.h"
+
+typedef void (*GLADapiproc)(void);
+typedef GLADapiproc (*GLADloadfunc)(const char *name);
+int
+gladLoadWGL(HDC hdc, GLADloadfunc load);
diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt
index 9040a6d98fc807bce250dcd117f71d5681f7f4d5..fb6522a16d7b3d839d52dbcca3892f79f8cdc574 100644
--- a/src/xrt/compositor/CMakeLists.txt
+++ b/src/xrt/compositor/CMakeLists.txt
@@ -54,6 +54,14 @@ 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
+		)
+	target_link_libraries(comp_client PRIVATE ${OPENGL_gl_LIBRARY})
+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_memobj_swapchain.c b/src/xrt/compositor/client/comp_gl_memobj_swapchain.c
index 2556f0d56b26604761d0c9f5659762321fb5a305..6bcbe9a767614a0c9c5ebf926c873f3ac22e8425 100644
--- a/src/xrt/compositor/client/comp_gl_memobj_swapchain.c
+++ b/src/xrt/compositor/client/comp_gl_memobj_swapchain.c
@@ -125,6 +125,55 @@ client_gl_memobj_swapchain_create(struct xrt_compositor *xc,
 		}
 	}
 
+	*out_cglsc = &sc->base;
+	return &sc->base.base.base;
+#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_WIN32_HANDLE)
+	struct client_gl_compositor *c = client_gl_compositor(xc);
+	(void)c;
+
+	if (xscn == NULL) {
+		return NULL;
+	}
+
+	GLuint binding_enum = 0;
+	GLuint tex_target = 0;
+	ogl_texture_target_for_swapchain_info(info, &tex_target, &binding_enum);
+	struct xrt_swapchain *native_xsc = &xscn->base;
+
+	struct client_gl_memobj_swapchain *sc = U_TYPED_CALLOC(struct client_gl_memobj_swapchain);
+	sc->base.base.base.destroy = client_gl_memobj_swapchain_destroy;
+	sc->base.base.base.reference.count = 1;
+	sc->base.base.base.image_count =
+	    native_xsc->image_count; // Fetch the number of images from the native swapchain.
+	sc->base.xscn = xscn;
+	sc->base.tex_target = tex_target;
+
+	sc->base.gl_compositor = c;
+
+	struct xrt_swapchain_gl *xscgl = &sc->base.base;
+	glGenTextures(native_xsc->image_count, xscgl->images);
+
+	glCreateMemoryObjectsEXT(native_xsc->image_count, &sc->memory[0]);
+	for (uint32_t i = 0; i < native_xsc->image_count; i++) {
+		glBindTexture(tex_target, xscgl->images[i]);
+
+		GLint dedicated = xscn->images[i].use_dedicated_allocation ? GL_TRUE : GL_FALSE;
+		glMemoryObjectParameterivEXT(sc->memory[i], GL_DEDICATED_MEMORY_OBJECT_EXT, &dedicated);
+		glImportMemoryWin32HandleEXT(sc->memory[i], xscn->images[i].size, GL_HANDLE_TYPE_OPAQUE_WIN32_EXT,
+		                    xscn->images[i].handle);
+
+		// We have consumed this now, make sure it's not freed again.
+		xscn->images[i].handle = XRT_GRAPHICS_BUFFER_HANDLE_INVALID;
+
+		if (info->array_size == 1) {
+			glTextureStorageMem2DEXT(xscgl->images[i], info->mip_count, (GLuint)info->format, info->width,
+			                         info->height, sc->memory[i], 0);
+		} else {
+			glTextureStorageMem3DEXT(xscgl->images[i], info->mip_count, (GLuint)info->format, info->width,
+			                         info->height, info->array_size, sc->memory[i], 0);
+		}
+	}
+
 	*out_cglsc = &sc->base;
 	return &sc->base.base.base;
 #else
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 0000000000000000000000000000000000000000..4cdc9ade64418efa3c8a3cb750771268026697e4
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_client.c
@@ -0,0 +1,252 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Win32 client side glue to compositor implementation.
+ * @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"
+
+///////////////////
+#include <glad/gl.h>
+
+static void *
+get_proc(const char *namez);
+
+#include <windows.h>
+static HMODULE libGL;
+
+typedef PROC(GLAD_API_PTR *PFNWGLGETPROCADDRESSPROC)(LPCSTR lpszProc);
+static PFNWGLGETPROCADDRESSPROC gladGetProcAddressPtr;
+
+static int
+open_gl(void)
+{
+	libGL = LoadLibraryW(L"opengl32.dll");
+	if (libGL != NULL) {
+		void (*tmp)(void);
+		tmp = (void (*)(void))GetProcAddress(libGL, "wglGetProcAddress");
+		gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC)tmp;
+		return gladGetProcAddressPtr != NULL;
+	}
+
+	return 0;
+}
+
+static void
+close_gl(void)
+{
+	if (libGL != NULL) {
+		FreeLibrary((HMODULE)libGL);
+		libGL = NULL;
+	}
+}
+
+static void *
+get_proc(const char *namez)
+{
+	void *result = NULL;
+	if (libGL == NULL)
+		return NULL;
+
+#if !defined(__APPLE__) && !defined(__HAIKU__)
+	if (gladGetProcAddressPtr != NULL) {
+		result = gladGetProcAddressPtr(namez);
+	}
+#endif
+	if (result == NULL) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+		result = (void *)GetProcAddress((HMODULE)libGL, namez);
+#else
+		result = dlsym(libGL, namez);
+#endif
+	}
+
+	return result;
+}
+
+int
+gladLoadGL2(void)
+{
+	int status = 0;
+
+	if (open_gl()) {
+		status = gladLoadGL(&get_proc);
+		close_gl();
+	}
+
+	return status;
+}
+///////////////////
+
+/*
+ *
+ * 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);
+
+	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");
+
+	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");
+
+	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);
+}
+
+struct client_gl_win32_compositor *
+client_gl_win32_compositor_create(struct xrt_compositor_native *xcn, HDC hDC, HGLRC hGLRC)
+{
+	gladLoadGL2();
+
+	// 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,
+	};
+
+
+	bool need_make_current = !context_matches(&current_ctx, &app_ctx);
+
+	U_LOG_T("GL Compositor create: need makeCurrent");
+
+	if (need_make_current && !context_make_current(&app_ctx)) {
+		U_LOG_E("Failed to make GLX context current");
+		// No need to restore on failure.
+		return NULL;
+	}
+
+	gladLoadGL2();
+
+	U_LOG_T("GL Compositor create: need makeCurrent");
+
+	if (need_make_current && !context_make_current(&current_ctx)) {
+		U_LOG_E("Failed to make old GLX context current!");
+	}
+
+#define CHECK_REQUIRED_EXTENSION(EXT)                                                                                  \
+	do {                                                                                                           \
+		if (!GLAD_##EXT) {                                                                                     \
+			U_LOG_E("%s - Required OpenGL extension " #EXT " not available", __func__);                    \
+			return NULL;                                                                                   \
+		}                                                                                                      \
+	} while (0)
+
+	CHECK_REQUIRED_EXTENSION(GL_EXT_memory_object);
+
+#undef CHECK_REQUIRED_EXTENSION
+
+	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;
+
+	if (!client_gl_compositor_init(&c->base, xcn, client_gl_context_begin, client_gl_context_end,
+	                               client_gl_memobj_swapchain_create, NULL)) {
+		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 0000000000000000000000000000000000000000..83ba6fe7f8d24bc9652939f87c72c0835a7a1a79
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_client.h
@@ -0,0 +1,60 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  OpenGL on Win32 client side glue to compositor header.
+ * @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;
+};
+
+/*!
+ * 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, HDC hDC, HGLRC 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 0000000000000000000000000000000000000000..3fe6402fbc3aac21c0ba99f5f19098bafae014bb
--- /dev/null
+++ b/src/xrt/compositor/client/comp_gl_win32_glue.c
@@ -0,0 +1,24 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Glue code to OpenGL win32 client side code.
+ * @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, HDC hDC, HGLRC hGLRC)
+{
+	struct client_gl_win32_compositor *xcc = client_gl_win32_compositor_create(xcn, hDC, hGLRC);
+
+	return &xcc->base.base;
+}
diff --git a/src/xrt/include/xrt/xrt_gfx_win32.h b/src/xrt/include/xrt/xrt_gfx_win32.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ad971c5f95bface8af482624ef61856f449dd2d
--- /dev/null
+++ b/src/xrt/include/xrt/xrt_gfx_win32.h
@@ -0,0 +1,30 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Header defining a XRT graphics provider.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup xrt_iface
+ */
+
+#pragma once
+
+#include "xrt/xrt_device.h"
+#include "xrt/xrt_compositor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Create an OpenGL compositor client using win32.
+ *
+ * @ingroup xrt_iface
+ * @public @memberof xrt_compositor_native
+ */
+struct xrt_compositor_gl *
+xrt_gfx_provider_create_gl_win32(struct xrt_compositor_native *xcn, HDC hDC, HGLRC hGLRC);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/xrt/state_trackers/oxr/CMakeLists.txt b/src/xrt/state_trackers/oxr/CMakeLists.txt
index 5d0bc54b8d359c1cc9cc4987f321acd2234bed99..cb1f6820c4dae2b192693a4b09e0ac69b8126376 100644
--- a/src/xrt/state_trackers/oxr/CMakeLists.txt
+++ b/src/xrt/state_trackers/oxr/CMakeLists.txt
@@ -64,6 +64,11 @@ if(XRT_HAVE_OPENGL_GLX AND XRT_HAVE_XLIB)
 	target_sources(st_oxr PRIVATE oxr_session_gfx_gl_xlib.c)
 endif()
 
+if(XRT_HAVE_OPENGL AND WIN32)
+	target_compile_definitions(st_oxr PRIVATE XR_USE_PLATFORM_WIN32)
+	target_sources(st_oxr PRIVATE oxr_session_gfx_gl_win32.c)
+endif()
+
 if(XRT_HAVE_EGL)
 	target_compile_definitions(st_oxr PRIVATE XR_USE_PLATFORM_EGL)
 	target_sources(st_oxr PRIVATE oxr_session_gfx_egl.c)
diff --git a/src/xrt/state_trackers/oxr/oxr_api_verify.h b/src/xrt/state_trackers/oxr/oxr_api_verify.h
index bd6cc5da37f8b0784e4cfe189a33957cb20d4063..e5d010fb7a513381f7d2b38d0358debfba7e74d4 100644
--- a/src/xrt/state_trackers/oxr/oxr_api_verify.h
+++ b/src/xrt/state_trackers/oxr/oxr_api_verify.h
@@ -260,6 +260,11 @@ XrResult
 oxr_verify_XrGraphicsBindingOpenGLXlibKHR(struct oxr_logger * /*log*/, const XrGraphicsBindingOpenGLXlibKHR * /*next*/);
 #endif // defined(XR_USE_PLATFORM_XLIB) && defined(XR_USE_GRAPHICS_API_OPENGL)
 
+#if defined(XR_USE_PLATFORM_WIN32) && defined(XR_USE_GRAPHICS_API_OPENGL)
+XrResult
+oxr_verify_XrGraphicsBindingOpenGLWin32KHR(struct oxr_logger * /*log*/, const XrGraphicsBindingOpenGLWin32KHR * /*next*/);
+#endif // defined(XR_USE_PLATFORM_WIN32) && defined(XR_USE_GRAPHICS_API_OPENGL)
+
 #if defined(XR_USE_GRAPHICS_API_VULKAN)
 XrResult
 oxr_verify_XrGraphicsBindingVulkanKHR(struct oxr_logger * /*log*/, const XrGraphicsBindingVulkanKHR * /*next*/);
diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h
index e5fc0d3917e765163ece02db66ad14dd0c621145..885361d6a02cc6abd4cdd0abdc3358e44b2baa4d 100644
--- a/src/xrt/state_trackers/oxr/oxr_objects.h
+++ b/src/xrt/state_trackers/oxr/oxr_objects.h
@@ -1007,6 +1007,15 @@ oxr_session_populate_gl_xlib(struct oxr_logger *log,
                              struct oxr_session *sess);
 #endif // XR_USE_PLATFORM_XLIB
 
+#ifdef XR_USE_PLATFORM_WIN32
+
+XrResult
+oxr_session_populate_gl_win32(struct oxr_logger *log,
+                              struct oxr_system *sys,
+                              XrGraphicsBindingOpenGLWin32KHR const *next,
+                              struct oxr_session *sess);
+#endif // XR_USE_PLATFORM_WIN32
+
 #endif // XR_USE_GRAPHICS_API_OPENGL
 
 #if defined(XR_USE_GRAPHICS_API_OPENGL) || defined(XR_USE_GRAPHICS_API_OPENGL_ES)
diff --git a/src/xrt/state_trackers/oxr/oxr_session.c b/src/xrt/state_trackers/oxr/oxr_session.c
index a3dce932c2d38243a84ee496c00c7092b2aaba40..0d84e0dec77f77ba0f1ab2d25fd6096584dbcc18 100644
--- a/src/xrt/state_trackers/oxr/oxr_session.c
+++ b/src/xrt/state_trackers/oxr/oxr_session.c
@@ -16,6 +16,10 @@
 #include "xrt/xrt_gfx_xlib.h"
 #endif // XR_USE_PLATFORM_XLIB
 
+#ifdef XR_USE_PLATFORM_WIN32
+#include "xrt/xrt_gfx_win32.h"
+#endif // XR_USE_PLATFORM_WIN32
+
 #ifdef XRT_HAVE_VULKAN
 #include "xrt/xrt_gfx_vk.h"
 #endif // XRT_HAVE_VULKAN
@@ -664,6 +668,21 @@ oxr_session_create_impl(struct oxr_logger *log,
 	}
 #endif
 
+#if defined(XR_USE_PLATFORM_WIN32) && defined(XR_USE_GRAPHICS_API_OPENGL)
+	XrGraphicsBindingOpenGLWin32KHR const *opengl_win32 = OXR_GET_INPUT_FROM_CHAIN(
+	    createInfo, XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR, XrGraphicsBindingOpenGLWin32KHR);
+	if (opengl_win32 != NULL) {
+		if (!sys->gotten_requirements) {
+			return oxr_error(log, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING,
+			                 "Has not called "
+			                 "xrGetOpenGLGraphicsRequirementsKHR");
+		}
+
+		OXR_SESSION_ALLOCATE(log, sys, *out_session);
+		OXR_ALLOCATE_NATIVE_COMPOSITOR(log, xsi, *out_session);
+		return oxr_session_populate_gl_win32(log, sys, opengl_win32, *out_session);
+	}
+#endif
 
 #if defined(XR_USE_PLATFORM_ANDROID) && defined(XR_USE_GRAPHICS_API_OPENGL_ES)
 	XrGraphicsBindingOpenGLESAndroidKHR const *opengles_android = OXR_GET_INPUT_FROM_CHAIN(
diff --git a/src/xrt/state_trackers/oxr/oxr_session_gfx_gl_win32.c b/src/xrt/state_trackers/oxr/oxr_session_gfx_gl_win32.c
new file mode 100644
index 0000000000000000000000000000000000000000..602c4327df19faf099e86ded35e959d29cdccd0e
--- /dev/null
+++ b/src/xrt/state_trackers/oxr/oxr_session_gfx_gl_win32.c
@@ -0,0 +1,49 @@
+// Copyright 2018-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Holds OpenGL-specific session functions.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup oxr_main
+ * @ingroup comp_client
+ */
+
+#ifndef XR_USE_PLATFORM_WIN32
+#error "Must build this file with WIN32 enabled!"
+#endif
+
+#ifndef XR_USE_GRAPHICS_API_OPENGL
+#error "Must build this file with OpenGL enabled!"
+#endif
+
+#include <stdlib.h>
+
+#include "util/u_misc.h"
+
+#include "oxr_objects.h"
+#include "oxr_logger.h"
+#include "oxr_two_call.h"
+#include "oxr_handle.h"
+
+#include "xrt/xrt_instance.h"
+#include "xrt/xrt_gfx_win32.h"
+
+
+XrResult
+oxr_session_populate_gl_win32(struct oxr_logger *log,
+                             struct oxr_system *sys,
+                             XrGraphicsBindingOpenGLWin32KHR const *next,
+                             struct oxr_session *sess)
+{
+	struct xrt_compositor_native *xcn = sess->xcn;
+	struct xrt_compositor_gl *xcgl = xrt_gfx_provider_create_gl_win32(xcn, next->hDC, next->hGLRC);
+
+	if (xcgl == NULL) {
+		return oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, "Failed to create an win32 client compositor");
+	}
+
+	sess->compositor = &xcgl->base;
+	sess->create_swapchain = oxr_swapchain_gl_create;
+
+	return XR_SUCCESS;
+}
diff --git a/src/xrt/state_trackers/oxr/oxr_swapchain.c b/src/xrt/state_trackers/oxr/oxr_swapchain.c
index 1e368cd3c193cc445893ad9fa5c164efdc66682e..d2034c32857b4cdb1f41b9764e665ec176cc2550 100644
--- a/src/xrt/state_trackers/oxr/oxr_swapchain.c
+++ b/src/xrt/state_trackers/oxr/oxr_swapchain.c
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 
 #include "xrt/xrt_gfx_xlib.h"
+#include "xrt/xrt_gfx_win32.h"
 
 #include "util/u_debug.h"
 #include "util/u_misc.h"
diff --git a/src/xrt/state_trackers/oxr/oxr_swapchain_gl.c b/src/xrt/state_trackers/oxr/oxr_swapchain_gl.c
index 7e2b858ef865f443749342fbf87fe07af7818f8f..8c7fdfb5618a7e0b41d1890ebdff6f4d64a05c64 100644
--- a/src/xrt/state_trackers/oxr/oxr_swapchain_gl.c
+++ b/src/xrt/state_trackers/oxr/oxr_swapchain_gl.c
@@ -12,6 +12,7 @@
 #include <stdlib.h>
 
 #include "xrt/xrt_gfx_xlib.h"
+#include "xrt/xrt_gfx_win32.h"
 #include "util/u_debug.h"
 
 #include "oxr_objects.h"
diff --git a/src/xrt/state_trackers/oxr/oxr_swapchain_vk.c b/src/xrt/state_trackers/oxr/oxr_swapchain_vk.c
index 8b164ef3b5dc57e108cfe8c2f15140227ee6f6ca..e6e0b52c364188cc6dea26a0b204c1f648eb846b 100644
--- a/src/xrt/state_trackers/oxr/oxr_swapchain_vk.c
+++ b/src/xrt/state_trackers/oxr/oxr_swapchain_vk.c
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 
 #include "xrt/xrt_gfx_xlib.h"
+#include "xrt/xrt_gfx_win32.h"
 #include "util/u_debug.h"
 
 #include "oxr_objects.h"
diff --git a/src/xrt/state_trackers/oxr/oxr_verify.c b/src/xrt/state_trackers/oxr/oxr_verify.c
index 689210dd2b3910ec9471834aa1e052116959cfd6..f3c050acdaa3915d808f62680a36f0888290a0b4 100644
--- a/src/xrt/state_trackers/oxr/oxr_verify.c
+++ b/src/xrt/state_trackers/oxr/oxr_verify.c
@@ -464,6 +464,15 @@ oxr_verify_XrSessionCreateInfo(struct oxr_logger *log,
 	}
 #endif // defined(OXR_HAVE_KHR_opengl_enable) && defined(XR_USE_PLATFORM_XLIB)
 
+#if defined(OXR_HAVE_KHR_opengl_enable) && defined(XR_USE_PLATFORM_WIN32)
+	XrGraphicsBindingOpenGLWin32KHR const *opengl_win32 = OXR_GET_INPUT_FROM_CHAIN(
+	    createInfo, XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR, XrGraphicsBindingOpenGLWin32KHR);
+	if (opengl_win32 != NULL) {
+		OXR_VERIFY_EXTENSION(log, inst, KHR_opengl_enable);
+		return oxr_verify_XrGraphicsBindingOpenGLWin32KHR(log, opengl_win32);
+	}
+#endif // defined(OXR_HAVE_KHR_opengl_enable) && defined(XR_USE_PLATFORM_WIN32)
+
 #if defined(OXR_HAVE_KHR_vulkan_enable) || defined(OXR_HAVE_KHR_vulkan_enable2)
 	/* XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR aliased to
 	 * XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR */
@@ -551,6 +560,27 @@ oxr_verify_XrGraphicsBindingOpenGLXlibKHR(struct oxr_logger *log, const XrGraphi
 
 #endif // defined(XR_USE_PLATFORM_XLIB) && defined(XR_USE_GRAPHICS_API_OPENGL)
 
+#if defined(XR_USE_PLATFORM_WIN32) && defined(XR_USE_GRAPHICS_API_OPENGL)
+
+XrResult
+oxr_verify_XrGraphicsBindingOpenGLWin32KHR(struct oxr_logger *log, const XrGraphicsBindingOpenGLWin32KHR *next)
+{
+	if (next->type != XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR) {
+		return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "Graphics binding has invalid type");
+	}
+
+	if (next->hDC == NULL) {
+		return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "hDC is NULL");
+	}
+
+	if (next->hGLRC == NULL) {
+		return oxr_error(log, XR_ERROR_VALIDATION_FAILURE, "hGLRC is NULL");
+	}
+
+	return XR_SUCCESS;
+}
+
+#endif // defined(XR_USE_PLATFORM_WIN32) && defined(XR_USE_GRAPHICS_API_OPENGL)
 
 #ifdef XR_USE_GRAPHICS_API_VULKAN