From fcc18cf83914549f16342bd0968dd489e08ac998 Mon Sep 17 00:00:00 2001
From: Ryan Pavlik <ryan.pavlik@collabora.com>
Date: Mon, 16 May 2022 15:06:36 -0500
Subject: [PATCH] comp: Add a basic "mock" native compositor for testing
 purposes

---
 src/xrt/compositor/CMakeLists.txt           |  10 +
 src/xrt/compositor/mock/mock_compositor.cpp | 204 +++++++++++
 src/xrt/compositor/mock/mock_compositor.h   | 360 ++++++++++++++++++++
 3 files changed, 574 insertions(+)
 create mode 100644 src/xrt/compositor/mock/mock_compositor.cpp
 create mode 100644 src/xrt/compositor/mock/mock_compositor.h

diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt
index 0113e44eb..9040a6d98 100644
--- a/src/xrt/compositor/CMakeLists.txt
+++ b/src/xrt/compositor/CMakeLists.txt
@@ -277,3 +277,13 @@ target_include_directories(comp_multi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 if(XRT_FEATURE_COMPOSITOR_MAIN)
 	target_link_libraries(comp_main PRIVATE comp_multi)
 endif()
+
+###
+# Mock native compositor
+add_library(comp_mock STATIC mock/mock_compositor.cpp mock/mock_compositor.h)
+target_include_directories(comp_mock PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(
+	comp_mock
+	PUBLIC xrt-interfaces
+	PRIVATE aux_util
+	)
diff --git a/src/xrt/compositor/mock/mock_compositor.cpp b/src/xrt/compositor/mock/mock_compositor.cpp
new file mode 100644
index 000000000..0221a3d46
--- /dev/null
+++ b/src/xrt/compositor/mock/mock_compositor.cpp
@@ -0,0 +1,204 @@
+// Copyright 2020-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief A mock native compositor to use when testing client compositors.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ */
+
+#include "mock_compositor.h"
+
+#include "util/u_misc.h"
+#include "util/u_handles.h"
+
+static void
+mock_compositor_swapchain_destroy(struct xrt_swapchain *xsc)
+{
+	struct mock_compositor_swapchain *mcsc = mock_compositor_swapchain(xsc);
+	struct mock_compositor *mc = mcsc->mc;
+
+	if (mc->swapchain_hooks.destroy) {
+		mc->swapchain_hooks.destroy(mc, mcsc);
+	}
+	for (uint32_t i = 0; i < mcsc->base.base.image_count; ++i) {
+		u_graphics_buffer_unref(&(mcsc->handles[i]));
+	}
+	free(xsc);
+}
+
+static xrt_result_t
+mock_compositor_swapchain_wait_image(struct xrt_swapchain *xsc, uint64_t timeout, uint32_t index)
+{
+	struct mock_compositor_swapchain *mcsc = mock_compositor_swapchain(xsc);
+	struct mock_compositor *mc = mcsc->mc;
+
+	if (mc->swapchain_hooks.wait_image) {
+		return mc->swapchain_hooks.wait_image(mc, mcsc, timeout, index);
+	}
+	mcsc->waited[index] = true;
+	return XRT_SUCCESS;
+}
+
+static xrt_result_t
+mock_compositor_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *out_index)
+{
+	struct mock_compositor_swapchain *mcsc = mock_compositor_swapchain(xsc);
+	struct mock_compositor *mc = mcsc->mc;
+
+	if (mc->swapchain_hooks.acquire_image) {
+		return mc->swapchain_hooks.acquire_image(mc, mcsc, out_index);
+	}
+	uint32_t index = mcsc->next_to_acquire;
+	mcsc->next_to_acquire = (mcsc->next_to_acquire + 1) % mcsc->base.base.image_count;
+	mcsc->acquired[index] = true;
+
+	return XRT_SUCCESS;
+}
+
+static xrt_result_t
+mock_compositor_swapchain_release_image(struct xrt_swapchain *xsc, uint32_t index)
+{
+	struct mock_compositor_swapchain *mcsc = mock_compositor_swapchain(xsc);
+	struct mock_compositor *mc = mcsc->mc;
+
+	if (mc->swapchain_hooks.acquire_image) {
+		return mc->swapchain_hooks.release_image(mc, mcsc, index);
+	}
+	mcsc->acquired[index] = false;
+	mcsc->waited[index] = false;
+
+	return XRT_SUCCESS;
+}
+
+
+static xrt_result_t
+mock_compositor_swapchain_create(struct xrt_compositor *xc,
+                                 const struct xrt_swapchain_create_info *info,
+                                 struct xrt_swapchain **out_xsc)
+{
+	struct mock_compositor *mc = mock_compositor(xc);
+	// Mini implementation of get_swapchain_create_properties to avoid an actual call causing confusing traces in
+	// the mock
+	uint32_t image_count = (0 != (info->create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE)) ? 1 : 3;
+	constexpr bool use_dedicated_allocation = false;
+
+
+	struct mock_compositor_swapchain *mcsc = U_TYPED_CALLOC(struct mock_compositor_swapchain);
+	mcsc->base.base.image_count = image_count;
+	mcsc->base.base.wait_image = mock_compositor_swapchain_wait_image;
+	mcsc->base.base.acquire_image = mock_compositor_swapchain_acquire_image;
+	mcsc->base.base.release_image = mock_compositor_swapchain_release_image;
+	mcsc->base.base.destroy = mock_compositor_swapchain_destroy;
+	mcsc->base.base.reference.count = 1;
+	mcsc->mc = mc;
+	mcsc->id = mc->next_id;
+	mcsc->info = *info;
+	mc->next_id++;
+
+	*out_xsc = &mcsc->base.base;
+	if (mc->compositor_hooks.create_swapchain) {
+		return mc->compositor_hooks.create_swapchain(mc, mcsc, info, out_xsc);
+	}
+
+	for (uint32_t i = 0; i < image_count; i++) {
+		mcsc->base.images[i].handle = XRT_GRAPHICS_BUFFER_HANDLE_INVALID;
+		mcsc->base.images[i].use_dedicated_allocation = use_dedicated_allocation;
+	}
+
+
+	return XRT_SUCCESS;
+}
+
+static xrt_result_t
+mock_compositor_swapchain_import(struct xrt_compositor *xc,
+                                 const struct xrt_swapchain_create_info *info,
+                                 struct xrt_image_native *native_images,
+                                 uint32_t image_count,
+                                 struct xrt_swapchain **out_xsc)
+{
+	struct mock_compositor *mc = mock_compositor(xc);
+	struct mock_compositor_swapchain *mcsc = U_TYPED_CALLOC(struct mock_compositor_swapchain);
+	mcsc->base.base.image_count = image_count;
+	mcsc->base.base.wait_image = mock_compositor_swapchain_wait_image;
+	mcsc->base.base.acquire_image = mock_compositor_swapchain_acquire_image;
+	mcsc->base.base.release_image = mock_compositor_swapchain_release_image;
+	mcsc->base.base.destroy = mock_compositor_swapchain_destroy;
+	mcsc->base.base.reference.count = 1;
+	mcsc->imported = true;
+	mcsc->mc = mc;
+	mcsc->id = mc->next_id;
+	mcsc->info = *info;
+	mc->next_id++;
+
+	*out_xsc = &mcsc->base.base;
+	if (mc->compositor_hooks.import_swapchain) {
+		return mc->compositor_hooks.import_swapchain(mc, mcsc, info, native_images, image_count, out_xsc);
+	}
+
+	for (uint32_t i = 0; i < image_count; i++) {
+		mcsc->handles[i] = native_images[i].handle;
+		mcsc->base.images[i] = native_images[i];
+	}
+
+	return XRT_SUCCESS;
+}
+
+static xrt_result_t
+mock_compositor_get_swapchain_create_properties(struct xrt_compositor *xc,
+                                                const struct xrt_swapchain_create_info *info,
+                                                struct xrt_swapchain_create_properties *xsccp)
+{
+	struct mock_compositor *mc = mock_compositor(xc);
+
+	if (mc->compositor_hooks.get_swapchain_create_properties) {
+		return mc->compositor_hooks.get_swapchain_create_properties(mc, info, xsccp);
+	}
+	// default "normal" impl
+	if (0 != (info->create & XRT_SWAPCHAIN_CREATE_STATIC_IMAGE)) {
+		xsccp->image_count = 1;
+	} else {
+		xsccp->image_count = 3;
+	}
+	return XRT_SUCCESS;
+}
+
+static void
+mock_compositor_destroy(struct xrt_compositor *xc)
+{
+	struct mock_compositor *mc = mock_compositor(xc);
+
+	if (mc->compositor_hooks.destroy) {
+		return mc->compositor_hooks.destroy(mc);
+	}
+	free(mc);
+}
+
+struct xrt_compositor_native *
+mock_create_native_compositor()
+{
+	struct mock_compositor *mc = U_TYPED_CALLOC(struct mock_compositor);
+	mc->base.base.get_swapchain_create_properties = mock_compositor_get_swapchain_create_properties;
+	mc->base.base.create_swapchain = mock_compositor_swapchain_create;
+	mc->base.base.import_swapchain = mock_compositor_swapchain_import;
+	// mc->base.base.create_semaphore = mock_compositor_semaphore_create;
+	// mc->base.base.begin_session = mock_compositor_begin_session;
+	// mc->base.base.end_session = mock_compositor_end_session;
+	// mc->base.base.wait_frame = mock_compositor_wait_frame;
+	// mc->base.base.begin_frame = mock_compositor_begin_frame;
+	// mc->base.base.discard_frame = mock_compositor_discard_frame;
+	// mc->base.base.layer_begin = mock_compositor_layer_begin;
+	// mc->base.base.layer_stereo_projection = mock_compositor_layer_stereo_projection;
+	// mc->base.base.layer_stereo_projection_depth = mock_compositor_layer_stereo_projection_depth;
+	// mc->base.base.layer_quad = mock_compositor_layer_quad;
+	// mc->base.base.layer_cube = mock_compositor_layer_cube;
+	// mc->base.base.layer_cylinder = mock_compositor_layer_cylinder;
+	// mc->base.base.layer_equirect1 = mock_compositor_layer_equirect1;
+	// mc->base.base.layer_equirect2 = mock_compositor_layer_equirect2;
+	// mc->base.base.layer_commit = mock_compositor_layer_commit;
+	// mc->base.base.layer_commit_with_semaphore = mock_compositor_layer_commit_with_semaphore;
+	// mc->base.base.poll_events = mock_compositor_poll_events;
+	mc->base.base.destroy = mock_compositor_destroy;
+
+	return &mc->base;
+}
diff --git a/src/xrt/compositor/mock/mock_compositor.h b/src/xrt/compositor/mock/mock_compositor.h
new file mode 100644
index 000000000..516481eda
--- /dev/null
+++ b/src/xrt/compositor/mock/mock_compositor.h
@@ -0,0 +1,360 @@
+// Copyright 2020-2022, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief A mock native compositor to use when testing client compositors.
+ * @author Ryan Pavlik <ryan.pavlik@collabora.com>
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ */
+
+#pragma once
+#include "xrt/xrt_compositor.h"
+#include "xrt/xrt_defines.h"
+#include "xrt/xrt_handles.h"
+
+#ifdef __cplusplus
+
+#include <type_traits>
+
+extern "C" {
+#endif
+
+struct mock_compositor_swapchain;
+/*!
+ * Mock implementation of a native compositor
+ * @implements xrt_compositor_native
+ */
+struct mock_compositor
+{
+	struct xrt_compositor_native base;
+
+	//! ID for next swapchain
+	uint32_t next_id;
+
+	//! Mock users can populate this pointer to use data from hooks
+	void *userdata;
+
+	/*!
+	 * Optional function pointers you can populate to hook into the behavior of the mock compositor implementation.
+	 *
+	 * Providing a function pointer will disable any built-in functionality in the mock for most of these fields.
+	 * While you can populate these with a lambda, because they're plain function pointers, you can't have any
+	 * captures, so use @ref mock_compositor::userdata to read or write any data from the outside world.
+	 */
+	struct
+	{
+		/*!
+		 * Optional function pointer for mock compositor, called during
+		 * @ref xrt_comp_get_swapchain_create_properties
+		 */
+		xrt_result_t (*get_swapchain_create_properties)(struct mock_compositor *mc,
+		                                                const struct xrt_swapchain_create_info *info,
+		                                                struct xrt_swapchain_create_properties *xsccp);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_create_swapchain
+		 *
+		 * Takes the extra parameter of the typed pointer to the in-progress swapchain @p mcsc , which is
+		 * allocated and has basic values populated for it, even if this function pointer is set.
+		 */
+		xrt_result_t (*create_swapchain)(struct mock_compositor *mc,
+		                                 struct mock_compositor_swapchain *mcsc,
+		                                 const struct xrt_swapchain_create_info *info,
+		                                 struct xrt_swapchain **out_xsc);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_import_swapchain
+		 *
+		 * Takes the extra parameter of the typed pointer to the in-progress swapchain @p mcsc , which is
+		 * allocated and has basic values populated for it, even if this function pointer is set. Does **not**
+		 * release the native images passed in if this function pointer is set, so you will have to do that
+		 * yourself.
+		 */
+		xrt_result_t (*import_swapchain)(struct mock_compositor *mc,
+		                                 struct mock_compositor_swapchain *mcsc,
+		                                 const struct xrt_swapchain_create_info *info,
+		                                 struct xrt_image_native *native_images,
+		                                 uint32_t image_count,
+		                                 struct xrt_swapchain **out_xsc);
+
+		// Mocks for the following not yet implemented
+#if 0
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_import_fence
+		 */
+		xrt_result_t (*import_fence)(struct mock_compositor *mc,
+		                             xrt_graphics_sync_handle_t handle,
+		                             struct xrt_compositor_fence **out_xcf);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_create_semaphore
+		 */
+		xrt_result_t (*create_semaphore)(struct mock_compositor *mc,
+		                                 xrt_graphics_sync_handle_t *out_handle,
+		                                 struct xrt_compositor_semaphore **out_xcsem);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_poll_events
+		 */
+		xrt_result_t (*poll_events)(struct mock_compositor *mc, union xrt_compositor_event *out_xce);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_begin_session
+		 */
+		xrt_result_t (*begin_session)(struct mock_compositor *mc, enum xrt_view_type view_type);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_end_session
+		 */
+		xrt_result_t (*end_session)(struct mock_compositor *mc);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_predict_frame
+		 */
+		xrt_result_t (*predict_frame)(struct mock_compositor *mc,
+		                              int64_t *out_frame_id,
+		                              uint64_t *out_wake_time_ns,
+		                              uint64_t *out_predicted_gpu_time_ns,
+		                              uint64_t *out_predicted_display_time_ns,
+		                              uint64_t *out_predicted_display_period_ns);
+
+		/*!
+		 *
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_mark_frame
+		 */
+		xrt_result_t (*mark_frame)(struct mock_compositor *mc,
+		                           int64_t frame_id,
+		                           enum xrt_compositor_frame_point point,
+		                           uint64_t when_ns);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_wait_frame
+		 */
+		xrt_result_t (*wait_frame)(struct mock_compositor *mc,
+		                           int64_t *out_frame_id,
+		                           uint64_t *out_predicted_display_time,
+		                           uint64_t *out_predicted_display_period);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_begin_frame
+		 */
+		xrt_result_t (*begin_frame)(struct mock_compositor *mc, int64_t frame_id);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_discard_frame
+		 */
+		xrt_result_t (*discard_frame)(struct mock_compositor *mc, int64_t frame_id);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_begin
+		 */
+		xrt_result_t (*layer_begin)(struct mock_compositor *mc,
+		                            int64_t frame_id,
+		                            uint64_t display_time_ns,
+		                            enum xrt_blend_mode env_blend_mode);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_stereo_projection
+		 */
+		xrt_result_t (*layer_stereo_projection)(struct mock_compositor *mc,
+		                                        struct xrt_device *xdev,
+		                                        struct xrt_swapchain *l_xsc,
+		                                        struct xrt_swapchain *r_xsc,
+		                                        const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref
+		 * xrt_comp_layer_stereo_projection_depth
+		 */
+		xrt_result_t (*layer_stereo_projection_depth)(struct mock_compositor *mc,
+		                                              struct xrt_device *xdev,
+		                                              struct xrt_swapchain *l_xsc,
+		                                              struct xrt_swapchain *r_xsc,
+		                                              struct xrt_swapchain *l_d_xsc,
+		                                              struct xrt_swapchain *r_d_xsc,
+		                                              const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_quad
+		 */
+		xrt_result_t (*layer_quad)(struct mock_compositor *mc,
+		                           struct xrt_device *xdev,
+		                           struct xrt_swapchain *xsc,
+		                           const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_cube
+		 */
+		xrt_result_t (*layer_cube)(struct mock_compositor *mc,
+		                           struct xrt_device *xdev,
+		                           struct xrt_swapchain *xsc,
+		                           const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_cylinder
+		 */
+		xrt_result_t (*layer_cylinder)(struct mock_compositor *mc,
+		                               struct xrt_device *xdev,
+		                               struct xrt_swapchain *xsc,
+		                               const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_equirect1
+		 */
+		xrt_result_t (*layer_equirect1)(struct mock_compositor *mc,
+		                                struct xrt_device *xdev,
+		                                struct xrt_swapchain *xsc,
+		                                const struct xrt_layer_data *data);
+
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_equirect2
+		 */
+		xrt_result_t (*layer_equirect2)(struct mock_compositor *mc,
+		                                struct xrt_device *xdev,
+		                                struct xrt_swapchain *xsc,
+		                                const struct xrt_layer_data *data);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_layer_commit
+		 */
+		xrt_result_t (*layer_commit)(struct mock_compositor *mc,
+		                             int64_t frame_id,
+		                             xrt_graphics_sync_handle_t sync_handle);
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref
+		 * xrt_comp_layer_commit_with_semaphore
+		 */
+		xrt_result_t (*layer_commit_with_semaphore)(struct mock_compositor *mc,
+		                                            int64_t frame_id,
+		                                            struct xrt_compositor_semaphore *xcsem,
+		                                            uint64_t value);
+#endif
+
+		/*!
+		 * Optional function pointer for mock compositor, called during @ref xrt_comp_destroy (before actual
+		 * destruction)
+		 *
+		 * The actual destruction is done by the mock implementation whether or not you populate this field.
+		 */
+		void (*destroy)(struct mock_compositor *mc);
+	} compositor_hooks;
+
+	/*!
+	 * Optional function pointers you can populate to hook into the behavior of the mock swapchain implementation.
+	 *
+	 * Providing a function pointer will disable any built-in functionality in the mock for most of these fields.
+	 * While you can populate these with a lambda, because they're plain function pointers, you can't have any
+	 * captures, so use @ref mock_compositor::userdata to read or write any data from the outside world.
+	 */
+	struct
+	{
+		/*!
+		 * Optional function pointer, called during @ref xrt_swapchain::destroy (before actual
+		 * destruction)
+		 *
+		 * The actual destruction is done by the mock implementation whether or not you populate this field.
+		 */
+		void (*destroy)(struct mock_compositor *mc, struct mock_compositor_swapchain *mcsc);
+
+		/*!
+		 * Optional function pointer, called during @ref xrt_swapchain::acquire_image
+		 */
+		xrt_result_t (*acquire_image)(struct mock_compositor *mc,
+		                              struct mock_compositor_swapchain *mcsc,
+		                              uint32_t *out_index);
+
+		/*!
+		 * Optional function pointer, called during @ref xrt_swapchain::wait_image
+		 */
+		xrt_result_t (*wait_image)(struct mock_compositor *mc,
+		                           struct mock_compositor_swapchain *mcsc,
+		                           uint64_t timeout,
+		                           uint32_t index);
+
+		/*!
+		 * Optional function pointer, called during @ref xrt_swapchain::release_image
+		 */
+		xrt_result_t (*release_image)(struct mock_compositor *mc,
+		                              struct mock_compositor_swapchain *mcsc,
+		                              uint32_t index);
+	} swapchain_hooks;
+};
+
+/*!
+ * @brief Cast a generic @ref xrt_compositor pointer (that you know externally is a @ref mock_compositor) to a @p
+ * mock_compositor pointer.
+ */
+static inline struct mock_compositor *
+mock_compositor(xrt_compositor *xc)
+{
+	return (struct mock_compositor *)(xc);
+}
+
+/*!
+ * Mock implementation of @ref xrt_swapchain_native
+ */
+struct mock_compositor_swapchain
+{
+	struct xrt_swapchain_native base;
+
+	//! A swapchain ID, assigned by create_swapchain/import_swapchain
+	uint32_t id;
+
+	//! Set if this swapchain was created by import_swapchain
+	bool imported;
+
+	//! Populated by copying the create info passed to create_swapchain/import_swapchain
+	xrt_swapchain_create_info info;
+	/**
+	 * Native handles for images.
+	 * Populated by the import_swapchain mock if not hooked.
+	 * Will be released/unreferenced at destruction by default.
+	 */
+	xrt_graphics_buffer_handle_t handles[XRT_MAX_SWAPCHAIN_IMAGES];
+
+	//! Modified by the default mock implementations of acquire_image and release_image
+	bool acquired[XRT_MAX_SWAPCHAIN_IMAGES];
+
+	//! Modified by the default mock implementations of wait_image and release_image
+	bool waited[XRT_MAX_SWAPCHAIN_IMAGES];
+
+	/**
+	 * The image ID that will next be acquired.
+	 *
+	 * The default minimal mock implementation just increments this, modulo image count, regardless of
+	 * acquire/wait/release status.
+	 */
+	uint32_t next_to_acquire;
+
+	//! non-owning pointer to parent
+	struct mock_compositor *mc;
+};
+
+/*!
+ * Cast a generic @ref xrt_swapchain pointer (that you know externally is a @ref mock_compositor_swapchain) to a @p
+ * mock_compositor_swapchain pointer.
+ */
+static inline struct mock_compositor_swapchain *
+mock_compositor_swapchain(xrt_swapchain *xsc)
+{
+	return (struct mock_compositor_swapchain *)(xsc);
+}
+
+/*!
+ * Create a mock implementation of @ref xrt_compositor_native.
+ *
+ * The returned value can be passed to @ref mock_compositor() to use the internals of the mock, e.g. to populate
+ * hooks to override mock behavior.
+ */
+struct xrt_compositor_native *
+mock_create_native_compositor();
+
+#ifdef __cplusplus
+
+static_assert(std::is_standard_layout<struct mock_compositor>::value);
+static_assert(std::is_standard_layout<struct mock_compositor_swapchain>::value);
+
+} // extern "C"
+#endif