The open source OpenXR runtime
1// Copyright 2019-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief The compositor compute based rendering code.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @ingroup comp_render
8 */
9
10#include "math/m_api.h"
11#include "math/m_matrix_4x4_f64.h"
12
13#include "render/render_interface.h"
14
15
16/*!
17 * Create a simplified projection matrix for timewarp.
18 */
19static void
20calc_projection(const struct xrt_fov *fov, struct xrt_matrix_4x4_f64 *result)
21{
22 const double tan_left = tan(fov->angle_left);
23 const double tan_right = tan(fov->angle_right);
24
25 const double tan_down = tan(fov->angle_down);
26 const double tan_up = tan(fov->angle_up);
27
28 const bool vulkan_projection_space_y = true;
29
30 const double tan_width = tan_right - tan_left;
31 const double tan_height = vulkan_projection_space_y // Projection space y direction:
32 ? (tan_down - tan_up) // Vulkan Y down
33 : (tan_up - tan_down); // OpenGL Y up
34
35 const double near_plane = 0.5;
36 const double far_plane = 1.5;
37
38 const double a11 = 2 / tan_width;
39 const double a22 = 2 / tan_height;
40
41 const double a31 = (tan_right + tan_left) / tan_width;
42 const double a32 = (tan_up + tan_down) / tan_height;
43
44 const double a33 = -far_plane / (far_plane - near_plane);
45 const double a43 = -(far_plane * near_plane) / (far_plane - near_plane);
46
47
48#if 0
49 // We skip a33 & a43 because we don't have depth.
50 (void)a33;
51 (void)a43;
52
53 // clang-format off
54 *result = (struct xrt_matrix_4x4_f64){
55 {
56 a11, 0, 0, 0,
57 0, a22, 0, 0,
58 a31, a32, -1, 0,
59 0, 0, 0, 1,
60 }
61 };
62 // clang-format on
63#else
64 /*
65 * Apparently the timewarp doesn't look good without this path being
66 * used. With the above it stretches out. I tried with the code to see
67 * if I could affect the depth where the view was placed but couldn't
68 * see to do it, which is a head scratcher.
69 */
70 // clang-format off
71 *result = (struct xrt_matrix_4x4_f64) {
72 .v = {
73 a11, 0, 0, 0,
74 0, a22, 0, 0,
75 a31, a32, a33, -1,
76 0, 0, a43, 0,
77 }
78 };
79 // clang-format on
80#endif
81}
82
83
84/*
85 *
86 * 'Exported' functions.
87 *
88 */
89
90uint32_t
91render_max_layers_capable(const struct vk_bundle *vk, bool use_compute, uint32_t desired_max_layers)
92{
93 /*!
94 * Graphics pipeline:
95 *
96 * This path has no relevant Vulkan device limits that would
97 * constrain the maximum number of layers (each layer uses a single descriptor
98 * set bound individually per draw).
99 */
100 if (!use_compute) {
101 // The min required by OpenXR spec is 16.
102 return MAX(desired_max_layers, 16);
103 }
104
105 /*!
106 * Compute pipeline:
107 *
108 * Clamp max layers based on compute pipeline descriptor limits.
109 *
110 * The compute path uses an array of combined image samplers, with
111 * @ref samplers_per_layer samplers needed per layer. We check both the
112 * per-stage sampler and sampled image limits, then calculate the
113 * maximum number of complete layers that fit within those limits.
114 */
115 uint32_t desired_image_sampler_count = desired_max_layers * RENDER_CS_MAX_SAMPLERS_PER_VIEW;
116
117 const uint32_t max_sizes[] = {
118 vk->limits.max_per_stage_descriptor_samplers,
119 vk->limits.max_per_stage_descriptor_sampled_images,
120 };
121 for (uint32_t i = 0; i < ARRAY_SIZE(max_sizes); ++i) {
122 desired_image_sampler_count = MIN(desired_image_sampler_count, max_sizes[i]);
123 }
124
125 const uint32_t calculated_max_layers = desired_image_sampler_count / RENDER_CS_MAX_SAMPLERS_PER_VIEW;
126
127 if (calculated_max_layers < 16) {
128 VK_WARN(vk,
129 "Device supports only %u compositor layers due to Vulkan limits. "
130 "which is below Vulkan minimum of 16. "
131 "This may indicate a driver bug. Attempting 16 anyway.",
132 calculated_max_layers);
133 }
134
135 // The min required by OpenXR spec is 16.
136 return MAX(calculated_max_layers, 16);
137}
138
139void
140render_calc_time_warp_matrix(const struct xrt_pose *src_pose,
141 const struct xrt_fov *src_fov,
142 const struct xrt_pose *new_pose,
143 struct xrt_matrix_4x4 *matrix)
144{
145 // Src projection matrix.
146 struct xrt_matrix_4x4_f64 src_proj;
147 calc_projection(src_fov, &src_proj);
148
149 // Src rotation matrix.
150 struct xrt_matrix_4x4_f64 src_rot_inv;
151 struct xrt_quat src_q = src_pose->orientation;
152 m_mat4_f64_orientation(&src_q, &src_rot_inv); // This is a model matrix, a inverted view matrix.
153
154 // New rotation matrix.
155 struct xrt_matrix_4x4_f64 new_rot, new_rot_inv;
156 struct xrt_quat new_q = new_pose->orientation;
157 m_mat4_f64_orientation(&new_q, &new_rot_inv); // This is a model matrix, a inverted view matrix.
158 m_mat4_f64_invert(&new_rot_inv, &new_rot); // Invert to make it a view matrix.
159
160 // Combine both rotation matrices to get difference.
161 struct xrt_matrix_4x4_f64 delta_rot, delta_rot_inv;
162 m_mat4_f64_multiply(&new_rot, &src_rot_inv, &delta_rot);
163 m_mat4_f64_invert(&delta_rot, &delta_rot_inv);
164
165 // Combine the source projection matrix and
166 struct xrt_matrix_4x4_f64 result;
167 m_mat4_f64_multiply(&src_proj, &delta_rot_inv, &result);
168
169 // Convert from f64 to f32.
170 for (int i = 0; i < 16; i++) {
171 matrix->v[i] = (float)result.v[i];
172 }
173}
174
175void
176render_calc_time_warp_projection(const struct xrt_fov *fov, struct xrt_matrix_4x4 *result)
177{
178 struct xrt_matrix_4x4_f64 tmp;
179 calc_projection(fov, &tmp);
180
181 for (int i = 0; i < 16; i++) {
182 result->v[i] = (float)tmp.v[i];
183 }
184}
185
186void
187render_calc_uv_to_tangent_lengths_rect(const struct xrt_fov *fov, struct xrt_normalized_rect *out_rect)
188{
189 const struct xrt_fov copy = *fov;
190
191 const double tan_left = tan(copy.angle_left);
192 const double tan_right = tan(copy.angle_right);
193
194 const double tan_down = tan(copy.angle_down);
195 const double tan_up = tan(copy.angle_up);
196
197 const double tan_width = tan_right - tan_left;
198 const double tan_height = tan_up - tan_down;
199
200 /*
201 * I do not know why we have to calculate the offsets like this, but
202 * this one is the one that seems to work with what is currently in the
203 * calc timewarp matrix function and the distortion shader. It works
204 * with Index (unbalanced left and right angles) and WMR (unbalanced up
205 * and down angles) so here it is. In so far it matches what the gfx
206 * and non-timewarp compute pipeline produces.
207 */
208 const double tan_offset_x = ((tan_right + tan_left) - tan_width) / 2;
209 const double tan_offset_y = (-(tan_up + tan_down) - tan_height) / 2;
210
211 struct xrt_normalized_rect transform = {
212 .x = (float)tan_offset_x,
213 .y = (float)tan_offset_y,
214 .w = (float)tan_width,
215 .h = (float)tan_height,
216 };
217
218 *out_rect = transform;
219}