mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-01 12:46:12 +00:00
c/main: Refactor out mirror_to_debug_gui code
This commit is contained in:
parent
f359826363
commit
44c81b6ddb
|
@ -185,6 +185,8 @@ if(XRT_MODULE_COMPOSITOR_MAIN)
|
||||||
main/comp_layer.c
|
main/comp_layer.c
|
||||||
main/comp_layer_renderer.h
|
main/comp_layer_renderer.h
|
||||||
main/comp_layer_renderer.c
|
main/comp_layer_renderer.c
|
||||||
|
main/comp_mirror_to_debug_gui.c
|
||||||
|
main/comp_mirror_to_debug_gui.h
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
comp_main
|
comp_main
|
||||||
|
|
321
src/xrt/compositor/main/comp_mirror_to_debug_gui.c
Normal file
321
src/xrt/compositor/main/comp_mirror_to_debug_gui.c
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
// Copyright 2019-2023, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Compositor mirroring code.
|
||||||
|
* @author Moses Turner <moses@collabora.com>
|
||||||
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||||
|
* @ingroup comp_main
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "math/m_mathinclude.h"
|
||||||
|
#include "main/comp_mirror_to_debug_gui.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Helper functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ensure_scratch(struct comp_mirror_to_debug_gui *m, struct vk_bundle *vk)
|
||||||
|
{
|
||||||
|
VkResult ret;
|
||||||
|
|
||||||
|
if (m->bounce.image != VK_NULL_HANDLE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||||
|
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
VkExtent2D extent = m->image_extent;
|
||||||
|
|
||||||
|
ret = vk_create_image_simple( //
|
||||||
|
vk, // vk_bundle
|
||||||
|
extent, // extent
|
||||||
|
format, // format
|
||||||
|
usage, // usage
|
||||||
|
&m->bounce.mem, // out_mem
|
||||||
|
&m->bounce.image); // out_image
|
||||||
|
if (ret != VK_SUCCESS) {
|
||||||
|
VK_ERROR(vk, "vk_create_image_simple: %s", vk_result_string(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 'Exported' functions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
VkResult
|
||||||
|
comp_mirror_init(struct comp_mirror_to_debug_gui *m, struct vk_bundle *vk, VkExtent2D extent)
|
||||||
|
{
|
||||||
|
VkResult ret;
|
||||||
|
|
||||||
|
// Do this init as early as possible.
|
||||||
|
u_sink_debug_init(&m->debug_sink);
|
||||||
|
|
||||||
|
double orig_width = extent.width;
|
||||||
|
double orig_height = extent.height;
|
||||||
|
|
||||||
|
double target_height = 1080;
|
||||||
|
|
||||||
|
double mul = target_height / orig_height;
|
||||||
|
|
||||||
|
// Casts seem to always round down; we don't want that here.
|
||||||
|
m->image_extent.width = (uint32_t)(round(orig_width * mul));
|
||||||
|
m->image_extent.height = (uint32_t)target_height;
|
||||||
|
|
||||||
|
|
||||||
|
// We want the images to have even widths/heights so that libx264 can encode them properly; no other reason.
|
||||||
|
if (m->image_extent.width % 2 == 1) {
|
||||||
|
m->image_extent.width += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk_image_readback_to_xf_pool_create( //
|
||||||
|
vk, // vk_bundle
|
||||||
|
m->image_extent, // extent
|
||||||
|
&m->pool, // out_pool
|
||||||
|
XRT_FORMAT_R8G8B8X8, // xrt_format
|
||||||
|
VK_FORMAT_R8G8B8A8_SRGB); // vk_format
|
||||||
|
|
||||||
|
ret = vk_cmd_pool_init(vk, &m->cmd_pool, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT);
|
||||||
|
if (ret != VK_SUCCESS) {
|
||||||
|
VK_ERROR(vk, "vk_cmd_pool_init: %s", vk_result_string(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
comp_mirror_add_debug_vars(struct comp_mirror_to_debug_gui *m, struct comp_compositor *c)
|
||||||
|
{
|
||||||
|
// Reset state.
|
||||||
|
m->push_every_frame_out_of_X = 2;
|
||||||
|
|
||||||
|
// Init widigts.
|
||||||
|
u_frame_times_widget_init(&m->push_frame_times, 0.f, 0.f);
|
||||||
|
comp_mirror_fixup_ui_state(m, c);
|
||||||
|
|
||||||
|
// Do the adding now.
|
||||||
|
u_var_add_root(m, "Readback", true);
|
||||||
|
|
||||||
|
u_var_add_bool(m, &c->mirroring_to_debug_gui, "Readback left eye to debug GUI");
|
||||||
|
u_var_add_i32(m, &m->push_every_frame_out_of_X, "Push 1 frame out of every X frames");
|
||||||
|
|
||||||
|
u_var_add_ro_f32(m, &m->push_frame_times.fps, "FPS (Readback)");
|
||||||
|
u_var_add_f32_timing(m, m->push_frame_times.debug_var, "Frame Times (Readback)");
|
||||||
|
|
||||||
|
u_var_add_sink_debug(m, &m->debug_sink, "Left view!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
comp_mirror_fixup_ui_state(struct comp_mirror_to_debug_gui *m, struct comp_compositor *c)
|
||||||
|
{
|
||||||
|
// One out of every zero frames is not what we want!
|
||||||
|
// Also one out of every negative two frames, etc. is nonsensical
|
||||||
|
if (m->push_every_frame_out_of_X < 1) {
|
||||||
|
m->push_every_frame_out_of_X = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float nominal_frame_interval_ms = (float)time_ns_to_ms_f(c->settings.nominal_frame_interval_ns);
|
||||||
|
|
||||||
|
m->target_frame_time_ms = (float)m->push_every_frame_out_of_X * nominal_frame_interval_ms;
|
||||||
|
|
||||||
|
m->push_frame_times.debug_var->reference_timing = m->target_frame_time_ms;
|
||||||
|
m->push_frame_times.debug_var->range = m->target_frame_time_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
comp_mirror_is_ready_and_active(struct comp_mirror_to_debug_gui *m,
|
||||||
|
struct comp_compositor *c,
|
||||||
|
uint64_t predicted_display_time_ns)
|
||||||
|
{
|
||||||
|
if (!c->mirroring_to_debug_gui || !u_sink_debug_is_active(&m->debug_sink)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double diff_ms = time_ns_to_ms_f(predicted_display_time_ns - m->last_push_ts_ns);
|
||||||
|
|
||||||
|
// Completely unscientific - lower values probably works fine too.
|
||||||
|
// I figure we don't have very many 500Hz displays and this woorks great for 120-144hz
|
||||||
|
double slop_ms = 2;
|
||||||
|
|
||||||
|
if (diff_ms < m->target_frame_time_ms - slop_ms) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the last time to the frame that is being displayed.
|
||||||
|
m->last_push_ts_ns = predicted_display_time_ns;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
comp_mirror_do_blit(struct comp_mirror_to_debug_gui *m,
|
||||||
|
struct vk_bundle *vk,
|
||||||
|
uint64_t predicted_display_time_ns,
|
||||||
|
VkImage from_image,
|
||||||
|
VkExtent2D from_extent)
|
||||||
|
{
|
||||||
|
VkResult ret;
|
||||||
|
|
||||||
|
struct vk_image_readback_to_xf *wrap = NULL;
|
||||||
|
|
||||||
|
if (!vk_image_readback_to_xf_pool_get_unused_frame(vk, m->pool, &wrap)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ensure_scratch(m, vk)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vk_cmd_pool *pool = &m->cmd_pool;
|
||||||
|
|
||||||
|
// For writing and submitting commands.
|
||||||
|
vk_cmd_pool_lock(pool);
|
||||||
|
|
||||||
|
VkCommandBuffer cmd;
|
||||||
|
ret = vk_cmd_pool_create_and_begin_cmd_buffer_locked(vk, pool, 0, &cmd);
|
||||||
|
if (ret != VK_SUCCESS) {
|
||||||
|
vk_cmd_pool_unlock(pool);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First mip view into the copy from image.
|
||||||
|
struct vk_cmd_first_mip_image copy_from_fm_image = {
|
||||||
|
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.base_array_layer = 0,
|
||||||
|
.image = from_image,
|
||||||
|
};
|
||||||
|
|
||||||
|
// First mip view into the bounce image.
|
||||||
|
struct vk_cmd_first_mip_image bounce_fm_image = {
|
||||||
|
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.base_array_layer = 0,
|
||||||
|
.image = m->bounce.image,
|
||||||
|
};
|
||||||
|
|
||||||
|
// First mip view into the target image.
|
||||||
|
struct vk_cmd_first_mip_image target_fm_image = {
|
||||||
|
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.base_array_layer = 0,
|
||||||
|
.image = wrap->image,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Blit arguments.
|
||||||
|
struct vk_cmd_blit_image_info blit_info = {
|
||||||
|
.src.old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
.src.src_access_mask = VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
.src.src_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
.src.fm_image = copy_from_fm_image,
|
||||||
|
|
||||||
|
.src.rect.offset = {0, 0},
|
||||||
|
.src.rect.extent = {from_extent.width, from_extent.height},
|
||||||
|
|
||||||
|
.dst.old_layout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
.dst.src_access_mask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.dst.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
.dst.fm_image = bounce_fm_image,
|
||||||
|
|
||||||
|
.dst.rect.offset = {0, 0},
|
||||||
|
.dst.rect.extent = {m->image_extent.width, m->image_extent.height},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk_cmd_blit_image_locked(vk, cmd, &blit_info);
|
||||||
|
|
||||||
|
// Copy arguments.
|
||||||
|
struct vk_cmd_copy_image_info copy_info = {
|
||||||
|
.src.old_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
.src.src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.src.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
.src.fm_image = bounce_fm_image,
|
||||||
|
|
||||||
|
.dst.old_layout = wrap->layout,
|
||||||
|
.dst.src_access_mask = VK_ACCESS_HOST_READ_BIT,
|
||||||
|
.dst.src_stage_mask = VK_PIPELINE_STAGE_HOST_BIT,
|
||||||
|
.dst.fm_image = target_fm_image,
|
||||||
|
|
||||||
|
.size.w = m->image_extent.width,
|
||||||
|
.size.h = m->image_extent.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk_cmd_copy_image_locked(vk, cmd, ©_info);
|
||||||
|
|
||||||
|
// Barrier arguments.
|
||||||
|
VkImageSubresourceRange first_color_level_subresource_range = {
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Barrier readback image to host so we can safely read
|
||||||
|
vk_cmd_image_barrier_locked( //
|
||||||
|
vk, // vk_bundle
|
||||||
|
cmd, // cmdbuffer
|
||||||
|
wrap->image, // image
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT, // srcAccessMask
|
||||||
|
VK_ACCESS_HOST_READ_BIT, // dstAccessMask
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // oldImageLayout
|
||||||
|
VK_IMAGE_LAYOUT_GENERAL, // newImageLayout
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
|
||||||
|
VK_PIPELINE_STAGE_HOST_BIT, // dstStageMask
|
||||||
|
first_color_level_subresource_range); // subresourceRange
|
||||||
|
|
||||||
|
// Done writing commands, submit to queue, waits for command to finish.
|
||||||
|
ret = vk_cmd_pool_end_submit_wait_and_free_cmd_buffer_locked(vk, pool, cmd);
|
||||||
|
|
||||||
|
// Done submitting commands.
|
||||||
|
vk_cmd_pool_unlock(pool);
|
||||||
|
|
||||||
|
// Check results from submit.
|
||||||
|
if (ret != VK_SUCCESS) {
|
||||||
|
//! @todo Better handling of error?
|
||||||
|
VK_ERROR(vk, "vk_cmd_pool_end_submit_wait_and_free_cmd_buffer_locked: %s", vk_result_string(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap->base_frame.source_timestamp = wrap->base_frame.timestamp = predicted_display_time_ns;
|
||||||
|
wrap->base_frame.source_id = m->sequence++;
|
||||||
|
|
||||||
|
struct xrt_frame *frame = &wrap->base_frame;
|
||||||
|
wrap = NULL;
|
||||||
|
|
||||||
|
u_sink_debug_push_frame(&m->debug_sink, frame);
|
||||||
|
|
||||||
|
u_frame_times_widget_push_sample(&m->push_frame_times, predicted_display_time_ns);
|
||||||
|
|
||||||
|
xrt_frame_reference(&frame, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
comp_mirror_fini(struct comp_mirror_to_debug_gui *m, struct vk_bundle *vk)
|
||||||
|
{
|
||||||
|
// Remove u_var root as early as possible.
|
||||||
|
u_var_remove_root(m);
|
||||||
|
|
||||||
|
// Left eye readback
|
||||||
|
vk_image_readback_to_xf_pool_destroy(vk, &m->pool);
|
||||||
|
|
||||||
|
if (m->bounce.image != VK_NULL_HANDLE) {
|
||||||
|
vk->vkFreeMemory(vk->device, m->bounce.mem, NULL);
|
||||||
|
vk->vkDestroyImage(vk->device, m->bounce.image, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command pool for readback code.
|
||||||
|
vk_cmd_pool_destroy(vk, &m->cmd_pool);
|
||||||
|
|
||||||
|
// The frame timing widigt.
|
||||||
|
u_frame_times_widget_teardown(&m->push_frame_times);
|
||||||
|
|
||||||
|
// Destroy as late as possible.
|
||||||
|
u_sink_debug_destroy(&m->debug_sink);
|
||||||
|
}
|
115
src/xrt/compositor/main/comp_mirror_to_debug_gui.h
Normal file
115
src/xrt/compositor/main/comp_mirror_to_debug_gui.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright 2019-2023, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Compositor mirroring code.
|
||||||
|
* @author Moses Turner <moses@collabora.com>
|
||||||
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||||
|
* @ingroup comp_main
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/u_sink.h"
|
||||||
|
#include "vk/vk_image_readback_to_xf_pool.h"
|
||||||
|
#include "main/comp_compositor.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Helper struct for mirroring the compositors rendering to the debug ui,
|
||||||
|
* which also enables recording. Currently embedded in @ref comp_renderer.
|
||||||
|
*
|
||||||
|
* @ingroup comp_main
|
||||||
|
*/
|
||||||
|
struct comp_mirror_to_debug_gui
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Hint: enable/disable is in c->mirroring_to_debug_gui. It is there
|
||||||
|
* because the ccomp_renderer struct is just a forward decl in the
|
||||||
|
* header and then defined properly in the comp_renderer.c file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct u_frame_times_widget push_frame_times;
|
||||||
|
|
||||||
|
float target_frame_time_ms;
|
||||||
|
uint64_t last_push_ts_ns;
|
||||||
|
int push_every_frame_out_of_X;
|
||||||
|
|
||||||
|
struct u_sink_debug debug_sink;
|
||||||
|
VkExtent2D image_extent;
|
||||||
|
uint64_t sequence;
|
||||||
|
|
||||||
|
struct vk_image_readback_to_xf_pool *pool;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
VkImage image;
|
||||||
|
VkDeviceMemory mem;
|
||||||
|
} bounce;
|
||||||
|
|
||||||
|
struct vk_cmd_pool cmd_pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Initialise the struct.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
VkResult
|
||||||
|
comp_mirror_init(struct comp_mirror_to_debug_gui *m, struct vk_bundle *vk, VkExtent2D extent);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* One time adding of the debug variables.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
comp_mirror_add_debug_vars(struct comp_mirror_to_debug_gui *m, struct comp_compositor *c);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Fixup various timing state.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
comp_mirror_fixup_ui_state(struct comp_mirror_to_debug_gui *m, struct comp_compositor *c);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Is this struct ready and capable of mirroring the image, can only
|
||||||
|
* call @ref comp_mirror_do_blit if this function has returned true.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
comp_mirror_is_ready_and_active(struct comp_mirror_to_debug_gui *m,
|
||||||
|
struct comp_compositor *c,
|
||||||
|
uint64_t predicted_display_time_ns);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Do the blit.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
comp_mirror_do_blit(struct comp_mirror_to_debug_gui *m,
|
||||||
|
struct vk_bundle *vk,
|
||||||
|
uint64_t predicted_display_time_ns,
|
||||||
|
VkImage from_image,
|
||||||
|
VkExtent2D from_extent);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Finalise the struct, frees and resources.
|
||||||
|
*
|
||||||
|
* @public @memberof comp_mirror_to_debug_gui
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
comp_mirror_fini(struct comp_mirror_to_debug_gui *m, struct vk_bundle *vk);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "main/comp_layer_renderer.h"
|
#include "main/comp_layer_renderer.h"
|
||||||
#include "main/comp_frame.h"
|
#include "main/comp_frame.h"
|
||||||
|
#include "main/comp_mirror_to_debug_gui.h"
|
||||||
|
|
||||||
#ifdef XRT_FEATURE_WINDOW_PEEK
|
#ifdef XRT_FEATURE_WINDOW_PEEK
|
||||||
#include "main/comp_window_peek.h"
|
#include "main/comp_window_peek.h"
|
||||||
|
@ -63,7 +64,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Private struct.
|
* Private struct(s).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -82,31 +83,7 @@ struct comp_renderer
|
||||||
struct comp_compositor *c;
|
struct comp_compositor *c;
|
||||||
struct comp_settings *settings;
|
struct comp_settings *settings;
|
||||||
|
|
||||||
struct
|
struct comp_mirror_to_debug_gui mirror_to_debug_gui;
|
||||||
{
|
|
||||||
// Hint: enable/disable is in c->mirroring_to_debug_gui. It's there because comp_renderer is just a
|
|
||||||
// forward decl to comp_compositor.c
|
|
||||||
|
|
||||||
struct u_frame_times_widget push_frame_times;
|
|
||||||
|
|
||||||
float target_frame_time_ms;
|
|
||||||
uint64_t last_push_ts_ns;
|
|
||||||
int push_every_frame_out_of_X;
|
|
||||||
|
|
||||||
struct u_sink_debug debug_sink;
|
|
||||||
VkExtent2D image_extent;
|
|
||||||
uint64_t sequence;
|
|
||||||
|
|
||||||
struct vk_image_readback_to_xf_pool *pool;
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
VkImage image;
|
|
||||||
VkDeviceMemory mem;
|
|
||||||
} bounce;
|
|
||||||
|
|
||||||
struct vk_cmd_pool cmd_pool;
|
|
||||||
} mirror_to_debug_gui;
|
|
||||||
|
|
||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
|
@ -566,41 +543,14 @@ renderer_init(struct comp_renderer *r, struct comp_compositor *c)
|
||||||
r->fenced_buffer = -1;
|
r->fenced_buffer = -1;
|
||||||
r->rtr_array = NULL;
|
r->rtr_array = NULL;
|
||||||
|
|
||||||
// Do this init as early as possible.
|
|
||||||
u_sink_debug_init(&r->mirror_to_debug_gui.debug_sink);
|
|
||||||
|
|
||||||
// Try to early-allocate these, in case we can.
|
// Try to early-allocate these, in case we can.
|
||||||
renderer_ensure_images_and_renderings(r, false);
|
renderer_ensure_images_and_renderings(r, false);
|
||||||
|
|
||||||
double orig_width = r->lr->extent.width;
|
|
||||||
double orig_height = r->lr->extent.height;
|
|
||||||
|
|
||||||
double target_height = 1080;
|
|
||||||
|
|
||||||
double mul = target_height / orig_height;
|
|
||||||
|
|
||||||
// Casts seem to always round down; we don't want that here.
|
|
||||||
r->mirror_to_debug_gui.image_extent.width = (uint32_t)(round(orig_width * mul));
|
|
||||||
r->mirror_to_debug_gui.image_extent.height = (uint32_t)target_height;
|
|
||||||
|
|
||||||
|
|
||||||
// We want the images to have even widths/heights so that libx264 can encode them properly; no other reason.
|
|
||||||
if (r->mirror_to_debug_gui.image_extent.width % 2 == 1) {
|
|
||||||
r->mirror_to_debug_gui.image_extent.width += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vk_bundle *vk = &r->c->base.vk;
|
struct vk_bundle *vk = &r->c->base.vk;
|
||||||
|
|
||||||
vk_image_readback_to_xf_pool_create( //
|
VkResult ret = comp_mirror_init(&r->mirror_to_debug_gui, vk, r->lr->extent);
|
||||||
vk, //
|
|
||||||
r->mirror_to_debug_gui.image_extent, //
|
|
||||||
&r->mirror_to_debug_gui.pool, //
|
|
||||||
XRT_FORMAT_R8G8B8X8, //
|
|
||||||
VK_FORMAT_R8G8B8A8_SRGB); //
|
|
||||||
|
|
||||||
VkResult ret = vk_cmd_pool_init(vk, &r->mirror_to_debug_gui.cmd_pool, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT);
|
|
||||||
if (ret != VK_SUCCESS) {
|
if (ret != VK_SUCCESS) {
|
||||||
COMP_ERROR(c, "vk_cmd_pool_init: %s", vk_result_string(ret));
|
COMP_ERROR(c, "comp_mirror_init: %s", vk_result_string(ret));
|
||||||
assert(false && "Whelp, can't return a error. But should never really fail.");
|
assert(false && "Whelp, can't return a error. But should never really fail.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -846,29 +796,14 @@ renderer_fini(struct comp_renderer *r)
|
||||||
{
|
{
|
||||||
struct vk_bundle *vk = &r->c->base.vk;
|
struct vk_bundle *vk = &r->c->base.vk;
|
||||||
|
|
||||||
// Remove u_var root as early as possible.
|
|
||||||
u_var_remove_root(r);
|
|
||||||
|
|
||||||
// Left eye readback
|
|
||||||
vk_image_readback_to_xf_pool_destroy(vk, &r->mirror_to_debug_gui.pool);
|
|
||||||
|
|
||||||
if (r->mirror_to_debug_gui.bounce.image != VK_NULL_HANDLE) {
|
|
||||||
vk->vkFreeMemory(vk->device, r->mirror_to_debug_gui.bounce.mem, NULL);
|
|
||||||
vk->vkDestroyImage(vk->device, r->mirror_to_debug_gui.bounce.image, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command pool for readback code.
|
|
||||||
vk_cmd_pool_destroy(vk, &r->mirror_to_debug_gui.cmd_pool);
|
|
||||||
|
|
||||||
// Command buffers
|
// Command buffers
|
||||||
renderer_close_renderings_and_fences(r);
|
renderer_close_renderings_and_fences(r);
|
||||||
|
|
||||||
|
// Do before layer render just in case it holds any references.
|
||||||
|
comp_mirror_fini(&r->mirror_to_debug_gui, vk);
|
||||||
|
|
||||||
|
// Do this after the mirror struct.
|
||||||
comp_layer_renderer_destroy(&(r->lr));
|
comp_layer_renderer_destroy(&(r->lr));
|
||||||
|
|
||||||
u_frame_times_widget_teardown(&r->mirror_to_debug_gui.push_frame_times);
|
|
||||||
|
|
||||||
// Destroy as late as possible.
|
|
||||||
u_sink_debug_destroy(&r->mirror_to_debug_gui.debug_sink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VkImageView
|
static VkImageView
|
||||||
|
@ -1755,210 +1690,6 @@ comp_renderer_set_cube_layer(struct comp_renderer *r,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
|
||||||
mirror_to_debug_gui_fixup_ui_state(struct comp_renderer *r)
|
|
||||||
{
|
|
||||||
// One out of every zero frames is not what we want!
|
|
||||||
// Also one out of every negative two frames, etc. is nonsensical
|
|
||||||
if (r->mirror_to_debug_gui.push_every_frame_out_of_X < 1) {
|
|
||||||
r->mirror_to_debug_gui.push_every_frame_out_of_X = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->mirror_to_debug_gui.target_frame_time_ms = (float)r->mirror_to_debug_gui.push_every_frame_out_of_X *
|
|
||||||
(float)time_ns_to_ms_f(r->c->settings.nominal_frame_interval_ns);
|
|
||||||
|
|
||||||
r->mirror_to_debug_gui.push_frame_times.debug_var->reference_timing =
|
|
||||||
r->mirror_to_debug_gui.target_frame_time_ms;
|
|
||||||
r->mirror_to_debug_gui.push_frame_times.debug_var->range = r->mirror_to_debug_gui.target_frame_time_ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
can_mirror_to_debug_gui(struct comp_renderer *r, uint64_t predicted_display_time_ns)
|
|
||||||
{
|
|
||||||
if (!r->c->mirroring_to_debug_gui || !u_sink_debug_is_active(&r->mirror_to_debug_gui.debug_sink)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double diff_ms = time_ns_to_ms_f(predicted_display_time_ns - r->mirror_to_debug_gui.last_push_ts_ns);
|
|
||||||
|
|
||||||
// Completely unscientific - lower values probably works fine too.
|
|
||||||
// I figure we don't have very many 500Hz displays and this woorks great for 120-144hz
|
|
||||||
double slop_ms = 2;
|
|
||||||
|
|
||||||
if (diff_ms < r->mirror_to_debug_gui.target_frame_time_ms - slop_ms) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the last time to the frame that is being displayed.
|
|
||||||
r->mirror_to_debug_gui.last_push_ts_ns = predicted_display_time_ns;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
mirror_to_debug_gui_ensure_scratch(struct comp_renderer *r)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (r->mirror_to_debug_gui.bounce.image != VK_NULL_HANDLE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vk_bundle *vk = &r->c->base.vk;
|
|
||||||
|
|
||||||
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
|
|
||||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
||||||
VkExtent2D extent = r->mirror_to_debug_gui.image_extent;
|
|
||||||
|
|
||||||
if (vk_create_image_simple( //
|
|
||||||
vk, // vk_bundle
|
|
||||||
extent, // extent
|
|
||||||
format, // format
|
|
||||||
usage, // usage
|
|
||||||
&r->mirror_to_debug_gui.bounce.mem, // out_mem
|
|
||||||
&r->mirror_to_debug_gui.bounce.image // out_image
|
|
||||||
) != VK_SUCCESS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mirror_to_debug_gui_do_blit(struct comp_renderer *r)
|
|
||||||
{
|
|
||||||
struct vk_bundle *vk = &r->c->base.vk;
|
|
||||||
VkResult ret;
|
|
||||||
|
|
||||||
struct vk_image_readback_to_xf *wrap = NULL;
|
|
||||||
|
|
||||||
if (!vk_image_readback_to_xf_pool_get_unused_frame(vk, r->mirror_to_debug_gui.pool, &wrap)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mirror_to_debug_gui_ensure_scratch(r)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vk_cmd_pool *pool = &r->mirror_to_debug_gui.cmd_pool;
|
|
||||||
|
|
||||||
// For writing and submitting commands.
|
|
||||||
vk_cmd_pool_lock(pool);
|
|
||||||
|
|
||||||
VkCommandBuffer cmd;
|
|
||||||
ret = vk_cmd_pool_create_and_begin_cmd_buffer_locked(vk, pool, 0, &cmd);
|
|
||||||
if (ret != VK_SUCCESS) {
|
|
||||||
vk_cmd_pool_unlock(pool);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First mip view into the copy from image.
|
|
||||||
struct vk_cmd_first_mip_image copy_from_fm_image = {
|
|
||||||
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.base_array_layer = 0,
|
|
||||||
.image = r->lr->framebuffers[0].image,
|
|
||||||
};
|
|
||||||
|
|
||||||
// First mip view into the bounce image.
|
|
||||||
struct vk_cmd_first_mip_image bounce_fm_image = {
|
|
||||||
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.base_array_layer = 0,
|
|
||||||
.image = r->mirror_to_debug_gui.bounce.image,
|
|
||||||
};
|
|
||||||
|
|
||||||
// First mip view into the target image.
|
|
||||||
struct vk_cmd_first_mip_image target_fm_image = {
|
|
||||||
.aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.base_array_layer = 0,
|
|
||||||
.image = wrap->image,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Blit arguments.
|
|
||||||
struct vk_cmd_blit_image_info blit_info = {
|
|
||||||
.src.old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
||||||
.src.src_access_mask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.src.src_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
||||||
.src.fm_image = copy_from_fm_image,
|
|
||||||
|
|
||||||
.src.rect.offset = {0, 0},
|
|
||||||
.src.rect.extent = {r->lr->extent.width, r->lr->extent.height},
|
|
||||||
|
|
||||||
.dst.old_layout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
||||||
.dst.src_access_mask = VK_ACCESS_TRANSFER_READ_BIT,
|
|
||||||
.dst.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
.dst.fm_image = bounce_fm_image,
|
|
||||||
|
|
||||||
.dst.rect.offset = {0, 0},
|
|
||||||
.dst.rect.extent = {r->mirror_to_debug_gui.image_extent.width, r->mirror_to_debug_gui.image_extent.height},
|
|
||||||
};
|
|
||||||
|
|
||||||
vk_cmd_blit_image_locked(vk, cmd, &blit_info);
|
|
||||||
|
|
||||||
// Copy arguments.
|
|
||||||
struct vk_cmd_copy_image_info copy_info = {
|
|
||||||
.src.old_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.src.src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.src.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
.src.fm_image = bounce_fm_image,
|
|
||||||
|
|
||||||
.dst.old_layout = wrap->layout,
|
|
||||||
.dst.src_access_mask = VK_ACCESS_HOST_READ_BIT,
|
|
||||||
.dst.src_stage_mask = VK_PIPELINE_STAGE_HOST_BIT,
|
|
||||||
.dst.fm_image = target_fm_image,
|
|
||||||
|
|
||||||
.size.w = r->mirror_to_debug_gui.image_extent.width,
|
|
||||||
.size.h = r->mirror_to_debug_gui.image_extent.height,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk_cmd_copy_image_locked(vk, cmd, ©_info);
|
|
||||||
|
|
||||||
// Barrier arguments.
|
|
||||||
VkImageSubresourceRange first_color_level_subresource_range = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Barrier readback image to host so we can safely read
|
|
||||||
vk_cmd_image_barrier_locked( //
|
|
||||||
vk, // vk_bundle
|
|
||||||
cmd, // cmdbuffer
|
|
||||||
wrap->image, // image
|
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT, // srcAccessMask
|
|
||||||
VK_ACCESS_HOST_READ_BIT, // dstAccessMask
|
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // oldImageLayout
|
|
||||||
VK_IMAGE_LAYOUT_GENERAL, // newImageLayout
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
|
|
||||||
VK_PIPELINE_STAGE_HOST_BIT, // dstStageMask
|
|
||||||
first_color_level_subresource_range); // subresourceRange
|
|
||||||
|
|
||||||
// Done writing commands, submit to queue, waits for command to finish.
|
|
||||||
ret = vk_cmd_pool_end_submit_wait_and_free_cmd_buffer_locked(vk, pool, cmd);
|
|
||||||
|
|
||||||
// Done submitting commands.
|
|
||||||
vk_cmd_pool_unlock(pool);
|
|
||||||
|
|
||||||
// Check results from submit.
|
|
||||||
if (ret != VK_SUCCESS) {
|
|
||||||
//! @todo Better handling of error?
|
|
||||||
COMP_ERROR(r->c, "Failed to mirror image");
|
|
||||||
}
|
|
||||||
|
|
||||||
wrap->base_frame.source_timestamp = wrap->base_frame.timestamp =
|
|
||||||
r->c->frame.rendering.predicted_display_time_ns;
|
|
||||||
wrap->base_frame.source_id = r->mirror_to_debug_gui.sequence++;
|
|
||||||
|
|
||||||
|
|
||||||
struct xrt_frame *frame = &wrap->base_frame;
|
|
||||||
wrap = NULL;
|
|
||||||
u_sink_debug_push_frame(&r->mirror_to_debug_gui.debug_sink, frame);
|
|
||||||
u_frame_times_widget_push_sample(&r->mirror_to_debug_gui.push_frame_times,
|
|
||||||
r->c->frame.rendering.predicted_display_time_ns);
|
|
||||||
|
|
||||||
xrt_frame_reference(&frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
comp_renderer_draw(struct comp_renderer *r)
|
comp_renderer_draw(struct comp_renderer *r)
|
||||||
{
|
{
|
||||||
|
@ -2039,9 +1770,14 @@ comp_renderer_draw(struct comp_renderer *r)
|
||||||
// Clear the rendered frame.
|
// Clear the rendered frame.
|
||||||
comp_frame_clear_locked(&c->frame.rendering);
|
comp_frame_clear_locked(&c->frame.rendering);
|
||||||
|
|
||||||
mirror_to_debug_gui_fixup_ui_state(r);
|
comp_mirror_fixup_ui_state(&r->mirror_to_debug_gui, c);
|
||||||
if (can_mirror_to_debug_gui(r, predicted_display_time_ns)) {
|
if (comp_mirror_is_ready_and_active(&r->mirror_to_debug_gui, c, predicted_display_time_ns)) {
|
||||||
mirror_to_debug_gui_do_blit(r);
|
comp_mirror_do_blit( //
|
||||||
|
&r->mirror_to_debug_gui, //
|
||||||
|
&c->base.vk, //
|
||||||
|
predicted_display_time_ns, //
|
||||||
|
r->lr->framebuffers[0].image, //
|
||||||
|
r->lr->extent); //
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2156,19 +1892,6 @@ void
|
||||||
comp_renderer_add_debug_vars(struct comp_renderer *self)
|
comp_renderer_add_debug_vars(struct comp_renderer *self)
|
||||||
{
|
{
|
||||||
struct comp_renderer *r = self;
|
struct comp_renderer *r = self;
|
||||||
r->mirror_to_debug_gui.push_every_frame_out_of_X = 2;
|
|
||||||
|
|
||||||
u_frame_times_widget_init(&r->mirror_to_debug_gui.push_frame_times, 0.f, 0.f);
|
comp_mirror_add_debug_vars(&r->mirror_to_debug_gui, r->c);
|
||||||
mirror_to_debug_gui_fixup_ui_state(r);
|
|
||||||
|
|
||||||
u_var_add_root(r, "Readback", true);
|
|
||||||
|
|
||||||
u_var_add_bool(r, &r->c->mirroring_to_debug_gui, "Readback left eye to debug GUI");
|
|
||||||
u_var_add_i32(r, &r->mirror_to_debug_gui.push_every_frame_out_of_X, "Push 1 frame out of every X frames");
|
|
||||||
|
|
||||||
u_var_add_ro_f32(r, &r->mirror_to_debug_gui.push_frame_times.fps, "FPS (Readback)");
|
|
||||||
u_var_add_f32_timing(r, r->mirror_to_debug_gui.push_frame_times.debug_var, "Frame Times (Readback)");
|
|
||||||
|
|
||||||
|
|
||||||
u_var_add_sink_debug(r, &r->mirror_to_debug_gui.debug_sink, "Left view!");
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue