From 90f708d91c31a82988a1950006e24cbbcf634154 Mon Sep 17 00:00:00 2001
From: Simon Zeni <simon@bl4ckb0ne.ca>
Date: Tue, 12 Jul 2022 14:31:57 -0400
Subject: [PATCH] xrt: Enable XR_KHR_composition_layer_cube

Co-authored-by: Bjorn Swenson <bjorn@collabora.com>
---
 CMakeLists.txt                                |  2 +-
 doc/changes/compositor/mr.1421.md             |  1 +
 doc/changes/xrt/mr.1421.md                    |  1 +
 src/xrt/compositor/CMakeLists.txt             | 19 ++++++---
 src/xrt/compositor/main/comp_compositor.c     | 12 +++++-
 src/xrt/compositor/main/comp_layer.c          |  4 +-
 src/xrt/compositor/main/comp_layer_renderer.c | 16 ++++++++
 src/xrt/compositor/main/comp_layer_renderer.h |  1 +
 src/xrt/compositor/main/comp_renderer.c       | 27 +++++++++++++
 src/xrt/compositor/main/comp_renderer.h       | 12 ++++++
 src/xrt/compositor/render/render_interface.h  |  3 ++
 src/xrt/compositor/render/render_shaders.c    | 22 ++++++++++
 src/xrt/compositor/shaders/cube.frag          | 27 +++++++++++++
 src/xrt/compositor/shaders/cube.vert          | 36 +++++++++++++++++
 src/xrt/compositor/util/comp_base.c           |  4 --
 src/xrt/include/xrt/xrt_compositor.h          |  1 -
 .../oxr/oxr_session_frame_end.c               | 40 ++++++++++++++++++-
 17 files changed, 212 insertions(+), 16 deletions(-)
 create mode 100644 doc/changes/compositor/mr.1421.md
 create mode 100644 doc/changes/xrt/mr.1421.md
 create mode 100644 src/xrt/compositor/shaders/cube.frag
 create mode 100644 src/xrt/compositor/shaders/cube.vert

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e7ec6bb6e..c490d3f19 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -312,7 +312,7 @@ if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH)
 	set(XRT_FEATURE_OPENXR_LAYER_DEPTH ON)
 endif()
 if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_CUBE)
-	set(XRT_FEATURE_OPENXR_LAYER_CUBE OFF)
+	set(XRT_FEATURE_OPENXR_LAYER_CUBE ON)
 endif()
 if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_CYLINDER)
 	set(XRT_FEATURE_OPENXR_LAYER_CYLINDER ON)
diff --git a/doc/changes/compositor/mr.1421.md b/doc/changes/compositor/mr.1421.md
new file mode 100644
index 000000000..b63755466
--- /dev/null
+++ b/doc/changes/compositor/mr.1421.md
@@ -0,0 +1 @@
+main: Render cube layer
diff --git a/doc/changes/xrt/mr.1421.md b/doc/changes/xrt/mr.1421.md
new file mode 100644
index 000000000..91ac6ee1c
--- /dev/null
+++ b/doc/changes/xrt/mr.1421.md
@@ -0,0 +1 @@
+Enable cube layer submission
diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt
index 72384dd53..a2242105f 100644
--- a/src/xrt/compositor/CMakeLists.txt
+++ b/src/xrt/compositor/CMakeLists.txt
@@ -111,11 +111,7 @@ endif()
 #
 
 if(XRT_HAVE_VULKAN)
-	spirv_shaders(
-		SHADER_HEADERS
-		SPIRV_VERSION
-		1.0 # Currently targeting Vulkan 1.0
-		SOURCES
+	set(SHADERS
 		shaders/clear.comp
 		shaders/distortion.comp
 		shaders/mesh.frag
@@ -127,6 +123,19 @@ if(XRT_HAVE_VULKAN)
 		shaders/equirect2.vert
 		shaders/equirect2.frag
 		)
+	if(XRT_FEATURE_OPENXR_LAYER_CUBE)
+		list(APPEND SHADERS
+			shaders/cube.vert
+			shaders/cube.frag
+			)
+	endif()
+
+	spirv_shaders(
+		SHADER_HEADERS
+		SPIRV_VERSION
+		1.0 # Currently targeting Vulkan 1.0
+		SOURCES ${SHADERS}
+		)
 
 	add_library(
 		comp_render STATIC
diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c
index 95b094397..a1943eb3d 100644
--- a/src/xrt/compositor/main/comp_compositor.c
+++ b/src/xrt/compositor/main/comp_compositor.c
@@ -283,13 +283,23 @@ do_graphics_layers(struct comp_compositor *c)
 			comp_renderer_set_equirect2_layer(c->r, i, image, data);
 		} break;
 #endif
+		case XRT_LAYER_CUBE: {
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+			struct xrt_layer_cube_data *cu = &layer->data.cube;
+			struct comp_swapchain_image *image;
+			image = &layer->sc_array[0]->images[cu->sub.image_index];
+			comp_renderer_set_cube_layer(c->r, i, image, data);
+#else
+			COMP_WARN(c, "XR_KHR_composition_layer_cube support not enabled");
+#endif
+		} break;
 #ifndef XRT_FEATURE_OPENXR_LAYER_EQUIRECT1
 		case XRT_LAYER_EQUIRECT1:
 #endif
 #ifndef XRT_FEATURE_OPENXR_LAYER_EQUIRECT2
 		case XRT_LAYER_EQUIRECT2:
 #endif
-		case XRT_LAYER_CUBE:
+		default:
 			// Should never end up here.
 			assert(false);
 		}
diff --git a/src/xrt/compositor/main/comp_layer.c b/src/xrt/compositor/main/comp_layer.c
index 6d848988c..546164627 100644
--- a/src/xrt/compositor/main/comp_layer.c
+++ b/src/xrt/compositor/main/comp_layer.c
@@ -340,9 +340,9 @@ comp_layer_draw(struct comp_render_layer *self,
 	case XRT_LAYER_QUAD:
 	case XRT_LAYER_CYLINDER:
 	case XRT_LAYER_EQUIRECT1:
-	case XRT_LAYER_EQUIRECT2: _update_mvp_matrix(self, eye, vp); break;
+	case XRT_LAYER_EQUIRECT2:
+	case XRT_LAYER_CUBE: _update_mvp_matrix(self, eye, vp); break;
 	case XRT_LAYER_STEREO_PROJECTION_DEPTH:
-	case XRT_LAYER_CUBE:
 		// Should never end up here.
 		assert(false);
 	}
diff --git a/src/xrt/compositor/main/comp_layer_renderer.c b/src/xrt/compositor/main/comp_layer_renderer.c
index 7737a3d9e..60cac9eea 100644
--- a/src/xrt/compositor/main/comp_layer_renderer.c
+++ b/src/xrt/compositor/main/comp_layer_renderer.c
@@ -335,6 +335,7 @@ static float plane_vertices[PLANE_VERTICES * 5] = {
 	-0.5,  0.5, 0, 0, 0,
 	-0.5, -0.5, 0, 0, 1,
 };
+
 // clang-format on
 
 static bool
@@ -390,6 +391,12 @@ _render_eye(struct comp_layer_renderer *self,
 			pipeline = self->pipeline_equirect1;
 			comp_layer_draw(self->layers[i], eye, pipeline, pipeline_layout, cmd_buffer, vertex_buffer,
 			                &vp_inv, &vp_inv);
+#if defined(XRT_FEATURE_OPENXR_LAYER_CUBE)
+		} else if (self->layers[i]->type == XRT_LAYER_CUBE) {
+			pipeline = self->pipeline_cube;
+			comp_layer_draw(self->layers[i], eye, pipeline, pipeline_layout, cmd_buffer, vertex_buffer,
+			                &vp_inv, &vp_inv);
+#endif
 		} else {
 			comp_layer_draw(self->layers[i], eye, pipeline, pipeline_layout, cmd_buffer, vertex_buffer,
 			                &vp_world, &vp_eye);
@@ -528,6 +535,12 @@ _init(struct comp_layer_renderer *self,
 		return false;
 	}
 
+#if defined(XRT_FEATURE_OPENXR_LAYER_CUBE)
+	if (!_init_graphics_pipeline(self, s->cube_vert, s->cube_frag, true, &self->pipeline_cube)) {
+		return false;
+	}
+#endif
+
 	if (!_init_vertex_buffer(self))
 		return false;
 
@@ -676,6 +689,9 @@ comp_layer_renderer_destroy(struct comp_layer_renderer **ptr_clr)
 	vk->vkDestroyPipeline(vk->device, self->pipeline_unpremultiplied_alpha, NULL);
 	vk->vkDestroyPipeline(vk->device, self->pipeline_equirect1, NULL);
 	vk->vkDestroyPipeline(vk->device, self->pipeline_equirect2, NULL);
+#if defined(XRT_FEATURE_OPENXR_LAYER_CUBE)
+	vk->vkDestroyPipeline(vk->device, self->pipeline_cube, NULL);
+#endif
 
 	for (uint32_t i = 0; i < ARRAY_SIZE(self->shader_modules); i++)
 		vk->vkDestroyShaderModule(vk->device, self->shader_modules[i], NULL);
diff --git a/src/xrt/compositor/main/comp_layer_renderer.h b/src/xrt/compositor/main/comp_layer_renderer.h
index d1dee6771..34e233fdf 100644
--- a/src/xrt/compositor/main/comp_layer_renderer.h
+++ b/src/xrt/compositor/main/comp_layer_renderer.h
@@ -40,6 +40,7 @@ struct comp_layer_renderer
 	VkPipeline pipeline_unpremultiplied_alpha;
 	VkPipeline pipeline_equirect1;
 	VkPipeline pipeline_equirect2;
+	VkPipeline pipeline_cube;
 	VkDescriptorSetLayout descriptor_set_layout;
 	VkDescriptorSetLayout descriptor_set_layout_equirect;
 
diff --git a/src/xrt/compositor/main/comp_renderer.c b/src/xrt/compositor/main/comp_renderer.c
index 4c1db38c0..cfe2bf2ef 100644
--- a/src/xrt/compositor/main/comp_renderer.c
+++ b/src/xrt/compositor/main/comp_renderer.c
@@ -1294,6 +1294,33 @@ comp_renderer_set_equirect2_layer(struct comp_renderer *r,
 }
 #endif
 
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+void
+comp_renderer_set_cube_layer(struct comp_renderer *r,
+                             uint32_t layer,
+                             struct comp_swapchain_image *image,
+                             struct xrt_layer_data *data)
+{
+
+	struct xrt_vec3 s = {1.0f, 1.0f, 1.0f};
+	struct xrt_matrix_4x4 model_matrix;
+	math_matrix_4x4_model(&data->cube.pose, &s, &model_matrix);
+
+	comp_layer_set_flip_y(r->lr->layers[layer], data->flip_y);
+
+	struct comp_render_layer *l = r->lr->layers[layer];
+	l->type = XRT_LAYER_CUBE;
+	l->visibility = data->cube.visibility;
+	l->flags = data->flags;
+	l->view_space = (data->flags & XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT) != 0;
+	l->transformation_ubo_binding = r->lr->transformation_ubo_binding;
+	l->texture_binding = r->lr->texture_binding;
+
+	comp_layer_update_descriptors(l, image->repeat_sampler,
+	                              get_image_view(image, data->flags, data->cube.sub.array_index));
+}
+#endif
+
 static void
 mirror_to_debug_gui_fixup_ui_state(struct comp_renderer *r)
 {
diff --git a/src/xrt/compositor/main/comp_renderer.h b/src/xrt/compositor/main/comp_renderer.h
index 27d9ff7a5..cfad48132 100644
--- a/src/xrt/compositor/main/comp_renderer.h
+++ b/src/xrt/compositor/main/comp_renderer.h
@@ -110,6 +110,18 @@ comp_renderer_set_equirect2_layer(struct comp_renderer *r,
                                   struct xrt_layer_data *data);
 #endif
 
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+/*!
+ * @public @memberof comp_renderer
+ * @ingroup comp_main
+ */
+void
+comp_renderer_set_cube_layer(struct comp_renderer *r,
+                             uint32_t layer,
+                             struct comp_swapchain_image *image,
+                             struct xrt_layer_data *data);
+#endif
+
 /*!
  * Allocate an internal array of per-layer data with the given number of elements.
  *
diff --git a/src/xrt/compositor/render/render_interface.h b/src/xrt/compositor/render/render_interface.h
index 2a1360016..ce2ecf433 100644
--- a/src/xrt/compositor/render/render_interface.h
+++ b/src/xrt/compositor/render/render_interface.h
@@ -86,6 +86,9 @@ struct render_shaders
 	VkShaderModule equirect2_vert;
 	VkShaderModule equirect2_frag;
 
+	VkShaderModule cube_vert;
+	VkShaderModule cube_frag;
+
 	VkShaderModule layer_vert;
 	VkShaderModule layer_frag;
 };
diff --git a/src/xrt/compositor/render/render_shaders.c b/src/xrt/compositor/render/render_shaders.c
index 859629d61..f1d9f7dfb 100644
--- a/src/xrt/compositor/render/render_shaders.c
+++ b/src/xrt/compositor/render/render_shaders.c
@@ -21,6 +21,8 @@
 #pragma GCC diagnostic ignored "-Wnewline-eof"
 #endif
 
+#include "xrt/xrt_config_build.h"
+
 #include "shaders/clear.comp.h"
 #include "shaders/distortion.comp.h"
 #include "shaders/layer.frag.h"
@@ -32,6 +34,11 @@
 #include "shaders/mesh.frag.h"
 #include "shaders/mesh.vert.h"
 
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+#include "shaders/cube.frag.h"
+#include "shaders/cube.vert.h"
+#endif
+
 #if defined(__GNUC__)
 #pragma GCC diagnostic pop
 #endif
@@ -118,6 +125,17 @@ render_shaders_load(struct render_shaders *s, struct vk_bundle *vk)
 	              sizeof(shaders_equirect2_frag), // size
 	              &s->equirect2_frag));           // out
 
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+	C(shader_load(vk,                        // vk_bundle
+	              shaders_cube_vert,         // data
+	              sizeof(shaders_cube_vert), // size
+	              &s->cube_vert));           // out
+	C(shader_load(vk,                        // vk_bundle
+	              shaders_cube_frag,         // data
+	              sizeof(shaders_cube_frag), // size
+	              &s->cube_frag));           // out
+#endif
+
 	C(shader_load(vk,                         // vk_bundle
 	              shaders_layer_vert,         // data
 	              sizeof(shaders_layer_vert), // size
@@ -149,6 +167,10 @@ render_shaders_close(struct render_shaders *s, struct vk_bundle *vk)
 	D(equirect1_frag);
 	D(equirect2_vert);
 	D(equirect2_frag);
+#ifdef XRT_FEATURE_OPENXR_LAYER_CUBE
+	D(cube_vert);
+	D(cube_frag);
+#endif
 	D(layer_vert);
 	D(layer_frag);
 
diff --git a/src/xrt/compositor/shaders/cube.frag b/src/xrt/compositor/shaders/cube.frag
new file mode 100644
index 000000000..518358e4d
--- /dev/null
+++ b/src/xrt/compositor/shaders/cube.frag
@@ -0,0 +1,27 @@
+// Copyright 2020 Simon Zeni <simon@bl4ckb0ne.ca>
+// Author: Simon Zeni <simon@bl4ckb0ne.ca>
+// Author: Bjorn Swenson <bjorn@collabora.com>
+// SPDX-License-Identifier: BSL-1.0
+
+#version 460
+
+layout (binding = 0, std140) uniform Transformation
+{
+	mat4 mvp;
+	ivec2 offset;
+	ivec2 extent;
+	bool flip_y;
+} ubo;
+
+layout (binding = 1) uniform samplerCube cube;
+
+layout (location = 0)  in vec2 uv;
+layout (location = 0) out vec4 out_color;
+
+void main ()
+{
+	vec2 frag_coord = vec2(uv) * 2 - 1;
+	vec4 view_dir = normalize(ubo.mvp * vec4(frag_coord.x, frag_coord.y, 1, 1));
+
+	out_color = texture(cube, view_dir.xyz);
+}
diff --git a/src/xrt/compositor/shaders/cube.vert b/src/xrt/compositor/shaders/cube.vert
new file mode 100644
index 000000000..d9d5066fc
--- /dev/null
+++ b/src/xrt/compositor/shaders/cube.vert
@@ -0,0 +1,36 @@
+// Copyright 2020 Simon Zeni <simon@bl4ckb0ne.ca>
+// Author: Simon Zeni <simon@bl4ckb0ne.ca>
+// Author: Bjorn Swenson <bjorn@collabora.com>
+// SPDX-License-Identifier: BSL-1.0
+
+#version 460
+
+layout (binding = 0, std140) uniform Transformation
+{
+	mat4 mvp;
+	ivec2 offset;
+	ivec2 extent;
+	bool flip_y;
+} transformation;
+
+layout (location = 0)  in vec3 position;
+layout (location = 1)  in vec2 uv;
+layout (location = 0) out vec2 out_uv;
+
+const mat4 mvp = mat4(
+	2, 0, 0, 0,
+	0, 2, 0, 0,
+	0, 0, 1, 0,
+	0, 0, 0, 1
+);
+
+void main() {
+	gl_Position = mvp * vec4(position, 1.0);
+	gl_Position.y *= -1.;
+
+	out_uv = uv;
+
+	if (transformation.flip_y) {
+		out_uv.y = 1.0 - out_uv.y;
+	}
+}
diff --git a/src/xrt/compositor/util/comp_base.c b/src/xrt/compositor/util/comp_base.c
index ee63b89c4..b03946d69 100644
--- a/src/xrt/compositor/util/comp_base.c
+++ b/src/xrt/compositor/util/comp_base.c
@@ -177,11 +177,7 @@ base_layer_cube(struct xrt_compositor *xc,
                 struct xrt_swapchain *xsc,
                 const struct xrt_layer_data *data)
 {
-#if 0
 	return do_single_layer(xc, xdev, xsc, data);
-#else
-	return XRT_SUCCESS; //! @todo Implement
-#endif
 }
 
 static xrt_result_t
diff --git a/src/xrt/include/xrt/xrt_compositor.h b/src/xrt/include/xrt/xrt_compositor.h
index 8fa4c2ba5..5697b561f 100644
--- a/src/xrt/include/xrt/xrt_compositor.h
+++ b/src/xrt/include/xrt/xrt_compositor.h
@@ -250,7 +250,6 @@ struct xrt_layer_cube_data
 	struct xrt_sub_image sub;
 
 	struct xrt_pose pose;
-	uint32_t image_array_index;
 };
 
 /*!
diff --git a/src/xrt/state_trackers/oxr/oxr_session_frame_end.c b/src/xrt/state_trackers/oxr/oxr_session_frame_end.c
index a9a67f649..0e1945c58 100644
--- a/src/xrt/state_trackers/oxr/oxr_session_frame_end.c
+++ b/src/xrt/state_trackers/oxr/oxr_session_frame_end.c
@@ -1034,7 +1034,7 @@ submit_projection_layer(struct oxr_session *sess,
 	return XR_SUCCESS;
 }
 
-static void
+static XrResult
 submit_cube_layer(struct oxr_session *sess,
                   struct xrt_compositor *xc,
                   struct oxr_logger *log,
@@ -1044,7 +1044,43 @@ submit_cube_layer(struct oxr_session *sess,
                   uint64_t oxr_timestamp,
                   uint64_t xrt_timestamp)
 {
-	// Not implemented
+	struct oxr_swapchain *sc = XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_swapchain *, cube->swapchain);
+	struct oxr_space *spc = XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_space *, cube->space);
+
+	struct xrt_layer_data data = {0};
+
+	data.type = XRT_LAYER_CUBE;
+	data.name = XRT_INPUT_GENERIC_HEAD_POSE;
+	data.timestamp = xrt_timestamp;
+	data.flags = convert_layer_flags(cube->layerFlags);
+
+	if (spc->space_type == OXR_SPACE_TYPE_REFERENCE_VIEW) {
+		data.flags |= XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT;
+	}
+
+	data.cube.visibility = convert_eye_visibility(cube->eyeVisibility);
+
+	data.cube.sub.image_index = sc->released.index;
+	data.cube.sub.array_index = cube->imageArrayIndex;
+
+	struct xrt_pose pose = {
+	    .orientation =
+	        {
+	            .x = cube->orientation.x,
+	            .y = cube->orientation.y,
+	            .z = cube->orientation.z,
+	            .w = cube->orientation.w,
+	        },
+	    .position = XRT_VEC3_ZERO,
+	};
+
+	if (!handle_space(log, sess, spc, &pose, inv_offset, oxr_timestamp, &data.cube.pose)) {
+		return XR_SUCCESS;
+	}
+
+	CALL_CHK(xrt_comp_layer_cube(xc, head, sc->swapchain, &data));
+
+	return XR_SUCCESS;
 }
 
 static XrResult