comp: Add a basic "mock" native compositor for testing purposes

This commit is contained in:
Ryan Pavlik 2022-05-16 15:06:36 -05:00 committed by Jakob Bornecrantz
parent 7ccf622ab3
commit fcc18cf839
3 changed files with 574 additions and 0 deletions

View file

@ -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
)

View file

@ -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;
}

View file

@ -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