2019-09-02 12:18:50 +00:00
|
|
|
// Copyright 2019, Collabora, Ltd.
|
|
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
/*!
|
|
|
|
* @file
|
|
|
|
* @brief A debugging scene.
|
|
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
|
|
* @ingroup gui
|
|
|
|
*/
|
|
|
|
|
2020-03-02 15:44:00 +00:00
|
|
|
#include "xrt/xrt_config_have.h"
|
2019-09-02 12:18:50 +00:00
|
|
|
#include "util/u_var.h"
|
|
|
|
#include "util/u_misc.h"
|
|
|
|
#include "util/u_sink.h"
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
#include "tracking/t_tracking.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "xrt/xrt_frame.h"
|
|
|
|
#include "xrt/xrt_prober.h"
|
2019-09-21 17:20:11 +00:00
|
|
|
#include "xrt/xrt_tracking.h"
|
2019-09-02 12:18:50 +00:00
|
|
|
#include "xrt/xrt_frameserver.h"
|
|
|
|
|
2020-01-25 16:39:57 +00:00
|
|
|
#include "math/m_api.h"
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
#include "gui_common.h"
|
|
|
|
#include "gui_imgui.h"
|
|
|
|
|
2020-03-06 00:46:35 +00:00
|
|
|
#include "imgui_monado/cimgui_monado.h"
|
|
|
|
|
|
|
|
#include <float.h>
|
2019-09-02 12:18:50 +00:00
|
|
|
|
|
|
|
struct debug_scene
|
|
|
|
{
|
|
|
|
struct gui_scene base;
|
|
|
|
struct xrt_frame_context *xfctx;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Internal functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
conv_rgb_f32_to_u8(struct xrt_colour_rgb_f32 *from,
|
|
|
|
struct xrt_colour_rgb_u8 *to)
|
|
|
|
{
|
|
|
|
to->r = (uint8_t)(from->r * 255.0f);
|
|
|
|
to->g = (uint8_t)(from->g * 255.0f);
|
|
|
|
to->b = (uint8_t)(from->b * 255.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
conv_rgb_u8_to_f32(struct xrt_colour_rgb_u8 *from,
|
|
|
|
struct xrt_colour_rgb_f32 *to)
|
|
|
|
{
|
|
|
|
to->r = from->r / 255.0f;
|
|
|
|
to->g = from->g / 255.0f;
|
|
|
|
to->b = from->b / 255.0f;
|
|
|
|
}
|
|
|
|
|
2020-01-25 16:39:57 +00:00
|
|
|
static void
|
|
|
|
handle_draggable_vec3_f32(const char *name, struct xrt_vec3 *v)
|
|
|
|
{
|
|
|
|
float min = -256.0f;
|
|
|
|
float max = 256.0f;
|
|
|
|
|
|
|
|
igDragFloat3(name, (float *)v, 0.005f, min, max, "%+f", 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_draggable_quat(const char *name, struct xrt_quat *q)
|
|
|
|
{
|
|
|
|
float min = -1.0f;
|
|
|
|
float max = 1.0f;
|
|
|
|
|
|
|
|
igDragFloat4(name, (float *)q, 0.005f, min, max, "%+f", 1.0f);
|
|
|
|
|
|
|
|
// Avoid invalid
|
|
|
|
if (q->x == 0.0f && q->y == 0.0f && q->z == 0.0f && q->w == 0.0f) {
|
|
|
|
q->w = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// And make sure it's a unit rotation.
|
|
|
|
math_quat_normalize(q);
|
|
|
|
}
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
struct draw_state
|
|
|
|
{
|
2019-10-09 15:22:34 +00:00
|
|
|
struct gui_program *p;
|
2019-09-02 12:18:50 +00:00
|
|
|
bool hidden;
|
|
|
|
};
|
|
|
|
|
2019-09-28 16:37:49 +00:00
|
|
|
static void
|
2019-10-09 15:22:34 +00:00
|
|
|
on_sink_var(const char *name, void *ptr, struct gui_program *p)
|
2019-09-28 16:37:49 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(p->texs); i++) {
|
|
|
|
struct gui_ogl_texture *tex = p->texs[i];
|
|
|
|
|
|
|
|
if (tex == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ptrdiff_t)tex->ptr != (ptrdiff_t)ptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!igCollapsingHeader(name, 0)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
gui_ogl_sink_update(tex);
|
|
|
|
|
|
|
|
igText("Sequence %u", (uint32_t)tex->seq);
|
|
|
|
char temp[512];
|
|
|
|
snprintf(temp, 512, "Half (%s)", tex->name);
|
|
|
|
igCheckbox(temp, &tex->half);
|
|
|
|
int w = tex->w / (tex->half ? 2 : 1);
|
|
|
|
int h = tex->h / (tex->half ? 2 : 1);
|
|
|
|
|
2019-11-14 17:39:13 +00:00
|
|
|
ImVec2 size = {(float)w, (float)h};
|
2019-09-28 16:37:49 +00:00
|
|
|
ImVec2 uv0 = {0, 0};
|
|
|
|
ImVec2 uv1 = {1, 1};
|
|
|
|
ImVec4 white = {1, 1, 1, 1};
|
|
|
|
ImTextureID id = (ImTextureID)(intptr_t)tex->id;
|
|
|
|
igImage(id, size, uv0, uv1, white, white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
static void
|
|
|
|
on_root_enter(const char *name, void *priv)
|
|
|
|
{
|
|
|
|
struct draw_state *state = (struct draw_state *)priv;
|
|
|
|
state->hidden = false;
|
|
|
|
|
|
|
|
igBegin(name, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2020-03-06 00:46:35 +00:00
|
|
|
static float
|
|
|
|
get_float_arr_val(void *_data, int _idx)
|
|
|
|
{
|
|
|
|
float *arr = _data;
|
|
|
|
return arr[_idx];
|
|
|
|
}
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
static void
|
|
|
|
on_elem(const char *name, enum u_var_kind kind, void *ptr, void *priv)
|
|
|
|
{
|
|
|
|
struct draw_state *state = (struct draw_state *)priv;
|
|
|
|
if (state->hidden && kind != U_VAR_KIND_GUI_HEADER) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const float drag_speed = 0.2f;
|
|
|
|
const float power = 1.0f;
|
|
|
|
const ImVec2 dummy = {0, 0};
|
|
|
|
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoInputs |
|
|
|
|
ImGuiColorEditFlags_NoLabel |
|
|
|
|
ImGuiColorEditFlags_PickerHueWheel;
|
|
|
|
(void)dummy;
|
|
|
|
ImGuiInputTextFlags i_flags = ImGuiInputTextFlags_None;
|
|
|
|
ImGuiInputTextFlags ro_i_flags = ImGuiInputTextFlags_ReadOnly;
|
|
|
|
|
|
|
|
switch (kind) {
|
2019-11-14 17:39:13 +00:00
|
|
|
case U_VAR_KIND_BOOL: igCheckbox(name, (bool *)ptr); break;
|
2019-09-02 12:18:50 +00:00
|
|
|
case U_VAR_KIND_RGB_F32:
|
|
|
|
igColorEdit3(name, (float *)ptr, flags);
|
|
|
|
igSameLine(0.0f, 4.0f);
|
|
|
|
igText("%s", name);
|
|
|
|
break;
|
|
|
|
case U_VAR_KIND_RGB_U8:;
|
|
|
|
struct xrt_colour_rgb_f32 tmp;
|
2019-11-14 17:39:13 +00:00
|
|
|
conv_rgb_u8_to_f32((struct xrt_colour_rgb_u8 *)ptr, &tmp);
|
2019-09-02 12:18:50 +00:00
|
|
|
on_elem(name, U_VAR_KIND_RGB_F32, &tmp, priv);
|
2019-11-14 17:39:13 +00:00
|
|
|
conv_rgb_f32_to_u8(&tmp, (struct xrt_colour_rgb_u8 *)ptr);
|
2019-09-02 12:18:50 +00:00
|
|
|
break;
|
|
|
|
case U_VAR_KIND_U8:
|
|
|
|
igDragScalar(name, ImGuiDataType_U8, ptr, drag_speed, NULL,
|
|
|
|
NULL, NULL, power);
|
|
|
|
break;
|
|
|
|
case U_VAR_KIND_I32:
|
|
|
|
igInputInt(name, (int *)ptr, 1, 10, i_flags);
|
|
|
|
break;
|
|
|
|
case U_VAR_KIND_VEC3_I32: igInputInt3(name, (int *)ptr, i_flags); break;
|
|
|
|
case U_VAR_KIND_F32:
|
2019-09-18 00:26:51 +00:00
|
|
|
igInputFloat(name, (float *)ptr, 1, 10, "%+f", i_flags);
|
2019-09-02 12:18:50 +00:00
|
|
|
break;
|
2020-03-06 00:46:35 +00:00
|
|
|
case U_VAR_KIND_F32_ARR: {
|
|
|
|
struct u_var_f32_arr *f32_arr = ptr;
|
|
|
|
int index = *f32_arr->index_ptr;
|
|
|
|
int length = f32_arr->length;
|
|
|
|
float *arr = (float *)f32_arr->data;
|
|
|
|
|
|
|
|
float w = igGetWindowContentRegionWidth();
|
|
|
|
ImVec2 graph_size = {w, 200};
|
|
|
|
|
|
|
|
float stats_min = FLT_MAX;
|
|
|
|
float stats_max = FLT_MAX;
|
|
|
|
|
|
|
|
igPlotLinesFnPtr(name, get_float_arr_val, arr, length, index,
|
|
|
|
NULL, stats_min, stats_max, graph_size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case U_VAR_KIND_TIMING: {
|
|
|
|
struct u_var_timing *frametime_arr = ptr;
|
|
|
|
struct u_var_f32_arr *f32_arr = &frametime_arr->values;
|
|
|
|
int index = *f32_arr->index_ptr;
|
|
|
|
int length = f32_arr->length;
|
|
|
|
float *arr = (float *)f32_arr->data;
|
|
|
|
|
|
|
|
float w = igGetWindowContentRegionWidth();
|
|
|
|
ImVec2 graph_size = {w, 200};
|
|
|
|
|
|
|
|
|
|
|
|
float stats_min = FLT_MAX;
|
|
|
|
float stats_max = 0;
|
|
|
|
|
|
|
|
for (int f = 0; f < length; f++) {
|
|
|
|
if (arr[f] < stats_min)
|
|
|
|
stats_min = arr[f];
|
|
|
|
if (arr[f] > stats_max)
|
|
|
|
stats_max = arr[f];
|
|
|
|
}
|
|
|
|
|
|
|
|
igPlotTimings(name, get_float_arr_val, arr, length, index, NULL,
|
|
|
|
0, stats_max, graph_size,
|
|
|
|
frametime_arr->reference_timing,
|
|
|
|
frametime_arr->center_reference_timing,
|
|
|
|
frametime_arr->range, frametime_arr->unit,
|
|
|
|
frametime_arr->dynamic_rescale);
|
|
|
|
break;
|
|
|
|
}
|
2019-09-02 12:18:50 +00:00
|
|
|
case U_VAR_KIND_VEC3_F32:
|
2019-09-18 00:26:51 +00:00
|
|
|
igInputFloat3(name, (float *)ptr, "%+f", i_flags);
|
2019-09-02 12:18:50 +00:00
|
|
|
break;
|
2020-01-25 16:39:57 +00:00
|
|
|
case U_VAR_KIND_POSE: {
|
|
|
|
struct xrt_pose *pose = (struct xrt_pose *)ptr;
|
|
|
|
char text[512];
|
|
|
|
snprintf(text, 512, "%s.position", name);
|
|
|
|
handle_draggable_vec3_f32(text, &pose->position);
|
|
|
|
snprintf(text, 512, "%s.orientation", name);
|
|
|
|
handle_draggable_quat(text, &pose->orientation);
|
|
|
|
break;
|
|
|
|
}
|
2019-09-02 12:18:50 +00:00
|
|
|
case U_VAR_KIND_RO_TEXT: igText("%s: '%s'", name, (char *)ptr); break;
|
|
|
|
case U_VAR_KIND_RO_I32:
|
|
|
|
igInputInt(name, (int *)ptr, 1, 10, ro_i_flags);
|
|
|
|
break;
|
|
|
|
case U_VAR_KIND_RO_VEC3_I32:
|
|
|
|
igInputInt3(name, (int *)ptr, ro_i_flags);
|
|
|
|
break;
|
|
|
|
case U_VAR_KIND_RO_F32:
|
2019-09-18 00:26:51 +00:00
|
|
|
igInputFloat(name, (float *)ptr, 1, 10, "%+f", ro_i_flags);
|
2019-09-02 12:18:50 +00:00
|
|
|
break;
|
|
|
|
case U_VAR_KIND_RO_VEC3_F32:
|
2019-09-18 00:26:51 +00:00
|
|
|
igInputFloat3(name, (float *)ptr, "%+f", ro_i_flags);
|
2019-09-02 12:18:50 +00:00
|
|
|
break;
|
2019-09-18 00:27:38 +00:00
|
|
|
case U_VAR_KIND_RO_QUAT_F32:
|
|
|
|
igInputFloat4(name, (float *)ptr, "%+f", ro_i_flags);
|
|
|
|
break;
|
2019-09-02 12:18:50 +00:00
|
|
|
case U_VAR_KIND_GUI_HEADER:
|
|
|
|
state->hidden = !igCollapsingHeader(name, 0);
|
|
|
|
break;
|
2019-09-28 16:37:49 +00:00
|
|
|
case U_VAR_KIND_SINK: on_sink_var(name, ptr, state->p); break;
|
2019-09-02 12:18:50 +00:00
|
|
|
default: igLabelText(name, "Unknown tag '%i'", kind); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_root_exit(const char *name, void *priv)
|
|
|
|
{
|
|
|
|
struct draw_state *state = (struct draw_state *)priv;
|
|
|
|
state->hidden = false;
|
|
|
|
|
|
|
|
igEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-09 15:22:34 +00:00
|
|
|
scene_render(struct gui_scene *scene, struct gui_program *p)
|
2019-09-02 12:18:50 +00:00
|
|
|
{
|
|
|
|
struct debug_scene *ds = (struct debug_scene *)scene;
|
|
|
|
(void)ds;
|
2019-09-28 16:37:49 +00:00
|
|
|
struct draw_state state = {p, false};
|
2019-09-02 12:18:50 +00:00
|
|
|
|
|
|
|
u_var_visit(on_root_enter, on_root_exit, on_elem, &state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-09 15:22:34 +00:00
|
|
|
scene_destroy(struct gui_scene *scene, struct gui_program *p)
|
2019-09-02 12:18:50 +00:00
|
|
|
{
|
|
|
|
struct debug_scene *ds = (struct debug_scene *)scene;
|
|
|
|
|
|
|
|
if (ds->xfctx != NULL) {
|
|
|
|
xrt_frame_context_destroy_nodes(ds->xfctx);
|
|
|
|
ds->xfctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ds);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-28 16:37:49 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Sink interception.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_root_enter_sink(const char *name, void *priv)
|
|
|
|
{}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_elem_sink(const char *name, enum u_var_kind kind, void *ptr, void *priv)
|
|
|
|
{
|
2019-10-09 15:22:34 +00:00
|
|
|
struct gui_program *p = (struct gui_program *)priv;
|
2019-09-28 16:37:49 +00:00
|
|
|
|
|
|
|
if (kind != U_VAR_KIND_SINK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-10 20:36:09 +00:00
|
|
|
if (p->xp == NULL || p->xp->tracking == NULL) {
|
2019-09-28 16:37:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct xrt_frame_context *xfctx = p->xp->tracking->xfctx;
|
|
|
|
struct xrt_frame_sink **xsink_ptr = (struct xrt_frame_sink **)ptr;
|
|
|
|
struct xrt_frame_sink *split = NULL;
|
|
|
|
|
|
|
|
p->texs[p->num_texs] = gui_ogl_sink_create(name, xfctx, &split);
|
|
|
|
p->texs[p->num_texs++]->ptr = ptr;
|
|
|
|
|
2019-10-19 22:14:53 +00:00
|
|
|
u_sink_create_to_r8g8b8_or_l8(xfctx, split, &split);
|
|
|
|
|
2019-09-28 16:37:49 +00:00
|
|
|
if (*xsink_ptr != NULL) {
|
|
|
|
u_sink_split_create(xfctx, split, *xsink_ptr, xsink_ptr);
|
|
|
|
} else {
|
|
|
|
*xsink_ptr = split;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_root_exit_sink(const char *name, void *priv)
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 'Exported' functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2019-10-09 15:22:34 +00:00
|
|
|
gui_scene_debug_video(struct gui_program *p,
|
2019-09-02 12:18:50 +00:00
|
|
|
struct xrt_frame_context *xfctx,
|
|
|
|
struct xrt_fs *xfs,
|
|
|
|
size_t mode)
|
|
|
|
{
|
|
|
|
struct debug_scene *ds = U_TYPED_CALLOC(struct debug_scene);
|
|
|
|
uint32_t num_texs = 0;
|
|
|
|
|
|
|
|
ds->base.render = scene_render;
|
|
|
|
ds->base.destroy = scene_destroy;
|
|
|
|
ds->xfctx = xfctx;
|
|
|
|
|
|
|
|
gui_scene_push_front(p, &ds->base);
|
|
|
|
|
|
|
|
struct xrt_frame_sink *xsink = NULL;
|
|
|
|
|
|
|
|
p->texs[num_texs++] = gui_ogl_sink_create("Stream", xfctx, &xsink);
|
|
|
|
u_sink_create_format_converter(xfctx, XRT_FORMAT_R8G8B8, xsink, &xsink);
|
|
|
|
u_sink_queue_create(xfctx, xsink, &xsink);
|
|
|
|
|
|
|
|
#ifdef XRT_HAVE_OPENCV
|
|
|
|
struct xrt_frame_sink *split = xsink;
|
|
|
|
xsink = NULL;
|
|
|
|
struct xrt_frame_sink *xsinks[4] = {NULL, NULL, NULL, NULL};
|
|
|
|
|
|
|
|
struct t_hsv_filter_params params = T_HSV_DEFAULT_PARAMS();
|
|
|
|
t_hsv_filter_create(xfctx, ¶ms, xsinks, &xsink);
|
|
|
|
u_sink_create_to_yuv_or_yuyv(xfctx, xsink, &xsink);
|
|
|
|
u_sink_queue_create(xfctx, xsink, &xsink);
|
|
|
|
|
|
|
|
u_sink_split_create(xfctx, split, xsink, &xsink);
|
|
|
|
#endif
|
|
|
|
|
2019-09-28 16:37:49 +00:00
|
|
|
// Create the sink interceptors.
|
|
|
|
u_var_visit(on_root_enter_sink, on_root_exit_sink, on_elem_sink, p);
|
|
|
|
|
2019-09-02 12:18:50 +00:00
|
|
|
// Now that we have setup a node graph, start it.
|
|
|
|
xrt_fs_stream_start(xfs, xsink, mode);
|
|
|
|
}
|
2019-09-21 17:20:11 +00:00
|
|
|
|
|
|
|
void
|
2019-10-09 15:22:34 +00:00
|
|
|
gui_scene_debug(struct gui_program *p)
|
2019-09-21 17:20:11 +00:00
|
|
|
{
|
|
|
|
struct debug_scene *ds = U_TYPED_CALLOC(struct debug_scene);
|
|
|
|
|
|
|
|
ds->base.render = scene_render;
|
|
|
|
ds->base.destroy = scene_destroy;
|
|
|
|
|
|
|
|
gui_scene_push_front(p, &ds->base);
|
|
|
|
|
2019-09-28 16:37:49 +00:00
|
|
|
// Create the sink interceptors.
|
2019-09-21 17:20:11 +00:00
|
|
|
u_var_visit(on_root_enter_sink, on_root_exit_sink, on_elem_sink, p);
|
|
|
|
}
|