File size: 9,670 Bytes
28451f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** @file   openxr_hmd.h
 *  @author Thomas Müller & Ingo Esser & Robert Menzel, NVIDIA
 *  @brief  Wrapper around the OpenXR API, providing access to
 *          per-eye framebuffers, lens parameters, visible area,
 *          view, hand, and eye poses, as well as controller inputs.
 */

#pragma once

#ifdef _WIN32
#  include <GL/gl3w.h>
#else
#  include <GL/glew.h>
#endif

#define XR_USE_GRAPHICS_API_OPENGL

#include <neural-graphics-primitives/common_device.cuh>

#include <openxr/openxr.h>
#include <xr_linear.h>
#include <xr_dependencies.h>
#include <openxr/openxr_platform.h>

#include <tiny-cuda-nn/gpu_memory.h>

#include <array>
#include <memory>
#include <vector>

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" //TODO: XR struct are uninitiaized apart from their type
#endif

namespace ngp {

enum class EEnvironmentBlendMode {
	Opaque = XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
	Additive = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
	AlphaBlend = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND,
};

inline std::string to_string(EEnvironmentBlendMode mode) {
	switch (mode) {
		case EEnvironmentBlendMode::Opaque: return "Opaque";
		case EEnvironmentBlendMode::Additive: return "Additive";
		case EEnvironmentBlendMode::AlphaBlend: return "Blend";
		default: throw std::runtime_error{"Invalid blend mode."};
	}
}

class OpenXRHMD {
public:
	enum class EControlFlow {
		Continue,
		Restart,
		Quit,
	};

	struct FrameInfo {
		struct View {
			GLuint framebuffer;
			XrCompositionLayerProjectionView view{XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW};
			XrCompositionLayerDepthInfoKHR depth_info{XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR};
			std::shared_ptr<Buffer2D<uint8_t>> hidden_area_mask = nullptr;
			mat4x3 pose;
		};
		struct Hand {
			mat4x3 pose;
			bool pose_active = false;
			vec2 thumbstick = vec2(0.0f);
			float grab_strength = 0.0f;
			bool grabbing = false;
			bool pressing = false;
			vec3 grab_pos;
			vec3 prev_grab_pos;
			vec3 drag() const {
				return grab_pos - prev_grab_pos;
			}
		};
		std::vector<View> views;
		Hand hands[2];
	};
	using FrameInfoPtr = std::shared_ptr<FrameInfo>;

	// RAII OpenXRHMD with OpenGL
#if defined(XR_USE_PLATFORM_WIN32)
	OpenXRHMD(HDC hdc, HGLRC hglrc);
#elif defined(XR_USE_PLATFORM_XLIB)
	OpenXRHMD(Display* xDisplay, uint32_t visualid, GLXFBConfig glxFBConfig, GLXDrawable glxDrawable, GLXContext glxContext);
#elif defined(XR_USE_PLATFORM_WAYLAND)
	OpenXRHMD(wl_display* display);
#endif

	virtual ~OpenXRHMD();

	// disallow copy / move
	OpenXRHMD(const OpenXRHMD&) = delete;
	OpenXRHMD& operator=(const OpenXRHMD&) = delete;
	OpenXRHMD(OpenXRHMD&&) = delete;
	OpenXRHMD& operator=(OpenXRHMD&&) = delete;

	void clear();

	// poll events, handle state changes, return control flow information
	EControlFlow poll_events();

	// begin OpenXR frame, return views to render
	FrameInfoPtr begin_frame();
	// must be called for each begin_frame
	void end_frame(FrameInfoPtr frame_info, float znear, float zfar, bool submit_depth);

	void set_environment_blend_mode(EEnvironmentBlendMode mode) {
		m_environment_blend_mode = mode;
	}

	EEnvironmentBlendMode environment_blend_mode() const {
		return m_environment_blend_mode;
	}

	const std::vector<EEnvironmentBlendMode>& supported_environment_blend_modes() const {
		return m_supported_environment_blend_modes;
	}

	const char* supported_environment_blend_modes_imgui_string() const {
		return m_supported_environment_blend_modes_imgui_string.data();
	}

	// if true call begin_frame and end_frame - does not imply visibility
	bool must_run_frame_loop() const {
		return
			m_session_state == XR_SESSION_STATE_READY ||
			m_session_state == XR_SESSION_STATE_SYNCHRONIZED ||
			m_session_state == XR_SESSION_STATE_VISIBLE ||
			m_session_state == XR_SESSION_STATE_FOCUSED;
	}

	// if true, VR is being rendered to the HMD
	bool is_visible() const {
		// XR_SESSION_STATE_VISIBLE -> app content is shown in HMD
		// XR_SESSION_STATE_FOCUSED -> VISIBLE + input is send to app
		return m_session_state == XR_SESSION_STATE_VISIBLE || m_session_state == XR_SESSION_STATE_FOCUSED;
	}

private:
	// steps of the init process, called from the constructor
	void init_create_xr_instance();
	void init_get_xr_system();
	void init_configure_xr_views();
	void init_check_for_xr_blend_mode();
	void init_xr_actions();

#if defined(XR_USE_PLATFORM_WIN32)
	void init_open_gl(HDC hdc, HGLRC hglrc);
#elif defined(XR_USE_PLATFORM_XLIB)
	void init_open_gl(Display* xDisplay, uint32_t visualid, GLXFBConfig glxFBConfig, GLXDrawable glxDrawable, GLXContext glxContext);
#elif defined(XR_USE_PLATFORM_WAYLAND)
	void init_open_gl(wl_display* display);
#endif

	void init_xr_session();
	void init_xr_spaces();
	void init_xr_swapchain_open_gl();
	void init_open_gl_shaders();

	// session state change
	void session_state_change(XrSessionState state, EControlFlow& flow);

	std::shared_ptr<Buffer2D<uint8_t>> rasterize_hidden_area_mask(uint32_t view_index, const XrCompositionLayerProjectionView& view);
	// system/instance
	XrInstance m_instance{XR_NULL_HANDLE};
	XrSystemId m_system_id = {};
	XrInstanceProperties m_instance_properties = {XR_TYPE_INSTANCE_PROPERTIES};
	XrSystemProperties m_system_properties = {XR_TYPE_SYSTEM_PROPERTIES};
	std::vector<XrApiLayerProperties> m_api_layer_properties;
	std::vector<XrExtensionProperties> m_instance_extension_properties;

	// view and blending
	XrViewConfigurationType m_view_configuration_type = {};
	XrViewConfigurationProperties m_view_configuration_properties = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES};
	std::vector<XrViewConfigurationView> m_view_configuration_views;
	std::vector<EEnvironmentBlendMode> m_supported_environment_blend_modes;
	std::vector<char> m_supported_environment_blend_modes_imgui_string;
	EEnvironmentBlendMode m_environment_blend_mode = EEnvironmentBlendMode::Opaque;

	// actions
	std::array<XrPath, 2> m_hand_paths;
	std::array<XrSpace, 2> m_hand_spaces;
	XrAction m_pose_action{XR_NULL_HANDLE};
	XrAction m_press_action{XR_NULL_HANDLE};
	XrAction m_grab_action{XR_NULL_HANDLE};

	// Two separate actions for Xbox controller support
	std::array<XrAction, 2> m_thumbstick_actions;

	XrActionSet m_action_set{XR_NULL_HANDLE};

#if defined(XR_USE_PLATFORM_WIN32)
	XrGraphicsBindingOpenGLWin32KHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR};
#elif defined(XR_USE_PLATFORM_XLIB)
	XrGraphicsBindingOpenGLXlibKHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR};
#elif defined(XR_USE_PLATFORM_WAYLAND)
	XrGraphicsBindingOpenGLWaylandKHR m_graphics_binding{XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR};
#endif

	XrSession m_session{XR_NULL_HANDLE};
	XrSessionState m_session_state{XR_SESSION_STATE_UNKNOWN};

	// reference space
	std::vector<XrReferenceSpaceType> m_reference_spaces;
	XrSpace m_space{XR_NULL_HANDLE};
	XrExtent2Df m_bounds;

	// swap chains
	struct Swapchain {
		Swapchain(XrSwapchainCreateInfo& rgba_create_info, XrSwapchainCreateInfo& depth_create_info, XrSession& session, XrInstance& xr_instance);
		Swapchain(const Swapchain&) = delete;
		Swapchain& operator=(const Swapchain&) = delete;
		Swapchain(Swapchain&& other) {
			*this = std::move(other);
		}
		Swapchain& operator=(Swapchain&& other) {
			std::swap(handle, other.handle);
			std::swap(depth_handle, other.depth_handle);
			std::swap(width, other.width);
			std::swap(height, other.height);
			images_gl = std::move(other.images_gl);
			depth_images_gl = std::move(other.depth_images_gl);
			framebuffers_gl = std::move(other.framebuffers_gl);
			return *this;
		}
		virtual ~Swapchain();

		void clear();

		XrSwapchain handle{XR_NULL_HANDLE};
		XrSwapchain depth_handle{XR_NULL_HANDLE};

		int32_t width = 0;
		int32_t height = 0;
		std::vector<XrSwapchainImageOpenGLKHR> images_gl;
		std::vector<XrSwapchainImageOpenGLKHR> depth_images_gl;
		std::vector<GLuint> framebuffers_gl;
	};

	int64_t m_swapchain_rgba_format = 0;
	std::vector<Swapchain> m_swapchains;

	bool m_supports_composition_layer_depth = false;
	int64_t m_swapchain_depth_format = 0;

	bool m_supports_hidden_area_mask = false;
	std::vector<std::shared_ptr<Buffer2D<uint8_t>>> m_hidden_area_masks;

	bool m_supports_eye_tracking = false;

	// frame data
	XrFrameState m_frame_state{XR_TYPE_FRAME_STATE};
	FrameInfoPtr m_previous_frame_info;

	GLuint m_hidden_area_mask_program = 0;

	// print more debug info during OpenXRs init:
	const bool m_print_api_layers = false;
	const bool m_print_extensions = false;
	const bool m_print_system_properties = false;
	const bool m_print_instance_properties = false;
	const bool m_print_view_configuration_types = false;
	const bool m_print_view_configuration_properties = false;
	const bool m_print_view_configuration_view = false;
	const bool m_print_environment_blend_modes = false;
	const bool m_print_available_swapchain_formats = false;
	const bool m_print_reference_spaces = false;
};

}

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif