Skip to content

Commit bb1f7e6

Browse files
committed
Update samples for Android support
1 parent 68efa87 commit bb1f7e6

File tree

5 files changed

+187
-37
lines changed

5 files changed

+187
-37
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
/generator/target
55
**/*.rs.bk
66
Cargo.lock
7+
*.so

openxr/Cargo.toml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,40 @@ ctrlc = "3.1.5"
3232
[target.'cfg(windows)'.dependencies]
3333
winapi = { version = "0.3", features = ["profileapi"] }
3434

35+
[target.'cfg(target_os = "android")'.dependencies]
36+
ndk-glue = "0.3"
37+
3538
[package.metadata.docs.rs]
3639
features = ["linked", "loaded", "mint"]
40+
41+
[[example]]
42+
name = "vulkan"
43+
44+
[[example]]
45+
name = "vulkan-android"
46+
path = "examples/vulkan.rs"
47+
crate-type = ["cdylib"]
48+
49+
# The following manifest metadata is used by cargo-apk to configure the example Android app for the Oculus Quest 1 and Quest 2.
50+
# It does not affect the openxr crate.
51+
[package.metadata.android]
52+
build_targets = ["aarch64-linux-android"]
53+
runtime_libs = "examples/libs"
54+
55+
[package.metadata.android.sdk]
56+
min_sdk_version = 21
57+
target_sdk_version = 30
58+
59+
[package.metadata.android.application.activity]
60+
theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen"
61+
config_changes = "density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode"
62+
launch_mode = "singleTask"
63+
orientation = "landscape"
64+
resizeable_activity = "false"
65+
66+
[[package.metadata.android.application.activity.intent_filter]]
67+
actions = ["android.intent.action.MAIN"]
68+
categories = [
69+
"com.oculus.intent.category.VR",
70+
"android.intent.category.LAUNCHER",
71+
]

openxr/examples/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Examples
2+
3+
## `hello`
4+
5+
Show information about the OpenXR runtime and XR headset.
6+
7+
## `vulkan`
8+
9+
Display a head-locked gradient spanning both eyes. Controllers position is reported on the terminal.
10+
11+
## `vulkan-android`
12+
13+
Same as `vulkan` but it can run on Android, specifically on the Oculus Quest and Quest 2. It shares the same source file.
14+
15+
* Install cargo-apk from [this PR](https://github.com/rust-windowing/android-ndk-rs/pull/138)
16+
* Get `libopenxr_loader.so` from the Oculus OpenXR Mobile SDK and add it to `openxr/examples/libs/arm64-v8a`
17+
* Run:
18+
19+
```sh
20+
cd openxr
21+
cargo apk run --example vulkan-android
22+
```
23+
24+
[Cargo.toml](../Cargo.toml) contains some metadata used by cargo-apk to generate the `AndroidManifest.xml`. In this example it is configured for the Oculus Quest and Quest 2, but changes are necessary to enable features like hand tracking or adding support for more headsets like the Oculus Go. You can read more details in the developer portal of your headset.

openxr/examples/hello.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
use openxr as xr;
22

3+
#[cfg_attr(target_os = "android", ndk_glue::main)]
34
fn main() {
45
#[cfg(feature = "linked")]
56
let entry = xr::Entry::linked();
67
#[cfg(not(feature = "linked"))]
78
let entry = xr::Entry::load()
89
.expect("couldn't find the OpenXR loader; try enabling the \"static\" feature");
910

11+
#[cfg(target_os = "android")]
12+
entry.initialize_android_loader();
13+
1014
let extensions = entry.enumerate_extensions().unwrap();
1115
println!("supported extensions: {:#?}", extensions);
1216
let layers = entry.enumerate_layers().unwrap();

openxr/examples/vulkan.rs

Lines changed: 123 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//! largely decouple its Vulkan and OpenXR components and handle errors gracefully.
77
88
use std::{
9+
ffi::{CStr, CString},
910
io::Cursor,
1011
sync::{
1112
atomic::{AtomicBool, Ordering},
@@ -22,7 +23,8 @@ use ash::{
2223
use openxr as xr;
2324

2425
#[allow(clippy::field_reassign_with_default)] // False positive, might be fixed 1.51
25-
fn main() {
26+
#[cfg_attr(target_os = "android", ndk_glue::main)]
27+
pub fn main() {
2628
// Handle interrupts gracefully
2729
let running = Arc::new(AtomicBool::new(true));
2830
let r = running.clone();
@@ -37,6 +39,9 @@ fn main() {
3739
let entry = xr::Entry::load()
3840
.expect("couldn't find the OpenXR loader; try enabling the \"static\" feature");
3941

42+
#[cfg(target_os = "android")]
43+
entry.initialize_android_loader().unwrap();
44+
4045
// OpenXR will fail to initialize if we ask for an extension that OpenXR can't provide! So we
4146
// need to check all our extensions before initializing OpenXR with them. Note that even if the
4247
// extension is present, it's still possible you may not be able to use it. For example: the
@@ -47,11 +52,19 @@ fn main() {
4752
// If a required extension isn't present, you want to ditch out here! It's possible something
4853
// like your rendering API might not be provided by the active runtime. APIs like OpenGL don't
4954
// have universal support.
50-
assert!(available_extensions.khr_vulkan_enable2);
55+
assert!(available_extensions.khr_vulkan_enable || available_extensions.khr_vulkan_enable2);
5156

5257
// Initialize OpenXR with the extensions we've found!
5358
let mut enabled_extensions = xr::ExtensionSet::default();
54-
enabled_extensions.khr_vulkan_enable2 = true;
59+
if available_extensions.khr_vulkan_enable2 {
60+
enabled_extensions.khr_vulkan_enable2 = true;
61+
} else {
62+
enabled_extensions.khr_vulkan_enable = true;
63+
}
64+
#[cfg(target_os = "android")]
65+
{
66+
enabled_extensions.khr_android_create_instance = true;
67+
}
5568
let xr_instance = entry
5669
.create_instance(
5770
&xr::ApplicationInfo {
@@ -90,6 +103,7 @@ fn main() {
90103
let reqs = xr_instance
91104
.graphics_requirements::<xr::Vulkan>(system)
92105
.unwrap();
106+
93107
if vk_target_version_xr < reqs.min_api_version_supported
94108
|| vk_target_version_xr.major() > reqs.max_api_version_supported.major()
95109
{
@@ -108,20 +122,46 @@ fn main() {
108122
.api_version(vk_target_version);
109123

110124
unsafe {
111-
let vk_instance = xr_instance
112-
.create_vulkan_instance(
113-
system,
114-
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
115-
&vk::InstanceCreateInfo::builder().application_info(&vk_app_info) as *const _
116-
as *const _,
125+
let vk_instance = if enabled_extensions.khr_vulkan_enable2 {
126+
let vk_instance = xr_instance
127+
.create_vulkan_instance(
128+
system,
129+
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
130+
&vk::InstanceCreateInfo::builder().application_info(&vk_app_info) as *const _
131+
as *const _,
132+
)
133+
.expect("XR error creating Vulkan instance")
134+
.map_err(vk::Result::from_raw)
135+
.expect("Vulkan error creating Vulkan instance");
136+
ash::Instance::load(
137+
vk_entry.static_fn(),
138+
vk::Instance::from_raw(vk_instance as _),
117139
)
118-
.expect("XR error creating Vulkan instance")
119-
.map_err(vk::Result::from_raw)
120-
.expect("Vulkan error creating Vulkan instance");
121-
let vk_instance = ash::Instance::load(
122-
vk_entry.static_fn(),
123-
vk::Instance::from_raw(vk_instance as _),
124-
);
140+
} else {
141+
let vk_instance_exts = xr_instance
142+
.vulkan_legacy_instance_extensions(system)
143+
.unwrap()
144+
.split(' ')
145+
.map(|x| CString::new(x).unwrap())
146+
.collect::<Vec<_>>();
147+
println!(
148+
"Required Vulkan instance extensions: {:?}",
149+
vk_instance_exts
150+
);
151+
let vk_instance_ext_ptrs = vk_instance_exts
152+
.iter()
153+
.map(|x| x.as_ptr())
154+
.collect::<Vec<_>>();
155+
156+
vk_entry
157+
.create_instance(
158+
&vk::InstanceCreateInfo::builder()
159+
.application_info(&vk_app_info)
160+
.enabled_extension_names(&vk_instance_ext_ptrs),
161+
None,
162+
)
163+
.expect("Vulkan error creating Vulkan instance")
164+
};
125165

126166
let vk_physical_device = vk::PhysicalDevice::from_raw(
127167
xr_instance
@@ -148,26 +188,72 @@ fn main() {
148188
})
149189
.expect("Vulkan device has no graphics queue");
150190

151-
let vk_device = xr_instance
152-
.create_vulkan_device(
153-
system,
154-
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
155-
vk_physical_device.as_raw() as _,
156-
&vk::DeviceCreateInfo::builder()
157-
.queue_create_infos(&[vk::DeviceQueueCreateInfo::builder()
158-
.queue_family_index(queue_family_index)
159-
.queue_priorities(&[1.0])
160-
.build()])
161-
.push_next(&mut vk::PhysicalDeviceMultiviewFeatures {
162-
multiview: vk::TRUE,
163-
..Default::default()
164-
}) as *const _ as *const _,
165-
)
166-
.expect("XR error creating Vulkan device")
167-
.map_err(vk::Result::from_raw)
168-
.expect("Vulkan error creating Vulkan device");
169-
let vk_device =
170-
ash::Device::load(vk_instance.fp_v1_0(), vk::Device::from_raw(vk_device as _));
191+
let vk_device = if enabled_extensions.khr_vulkan_enable2 {
192+
let vk_device = xr_instance
193+
.create_vulkan_device(
194+
system,
195+
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
196+
vk_physical_device.as_raw() as _,
197+
&vk::DeviceCreateInfo::builder()
198+
.queue_create_infos(&[vk::DeviceQueueCreateInfo::builder()
199+
.queue_family_index(queue_family_index)
200+
.queue_priorities(&[1.0])
201+
.build()])
202+
.push_next(&mut vk::PhysicalDeviceMultiviewFeatures {
203+
multiview: vk::TRUE,
204+
..Default::default()
205+
}) as *const _ as *const _,
206+
)
207+
.expect("XR error creating Vulkan device")
208+
.map_err(vk::Result::from_raw)
209+
.expect("Vulkan error creating Vulkan device");
210+
211+
ash::Device::load(vk_instance.fp_v1_0(), vk::Device::from_raw(vk_device as _))
212+
} else {
213+
let vk_device_exts = xr_instance
214+
.vulkan_legacy_device_extensions(system)
215+
.unwrap()
216+
.split(' ')
217+
.map(|x| CString::new(x).unwrap())
218+
.collect::<Vec<_>>();
219+
println!("Required Vulkan device extensions: {:?}", vk_device_exts);
220+
let vk_device_ext_ptrs = vk_device_exts
221+
.iter()
222+
.map(|x| x.as_ptr())
223+
.collect::<Vec<_>>();
224+
225+
// Check that we have the required Vulkan device extensions.
226+
let device_extensions = vk_instance
227+
.enumerate_device_extension_properties(vk_physical_device)
228+
.unwrap();
229+
for ext in &vk_device_exts {
230+
if !device_extensions.iter().any(|inst_ext| {
231+
CStr::from_ptr(inst_ext.extension_name.as_ptr()) == ext.as_c_str()
232+
}) {
233+
panic!(
234+
"OpenXR runtime requires missing Vulkan device extension {:?}",
235+
ext
236+
);
237+
}
238+
}
239+
240+
vk_instance
241+
.create_device(
242+
vk_physical_device,
243+
&vk::DeviceCreateInfo::builder()
244+
.queue_create_infos(&[vk::DeviceQueueCreateInfo::builder()
245+
.queue_family_index(queue_family_index)
246+
.queue_priorities(&[1.0])
247+
.build()])
248+
.enabled_extension_names(&vk_device_ext_ptrs)
249+
.push_next(&mut vk::PhysicalDeviceVulkan11Features {
250+
multiview: vk::TRUE,
251+
..Default::default()
252+
}),
253+
None,
254+
)
255+
.expect("Vulkan error creating Vulkan device")
256+
};
171257

172258
let queue = vk_device.get_device_queue(queue_family_index, 0);
173259

@@ -762,7 +848,7 @@ fn main() {
762848
println!("exiting cleanly");
763849
}
764850

765-
pub const COLOR_FORMAT: vk::Format = vk::Format::B8G8R8A8_SRGB;
851+
pub const COLOR_FORMAT: vk::Format = vk::Format::R8G8B8A8_SRGB;
766852
pub const VIEW_COUNT: u32 = 2;
767853
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
768854

0 commit comments

Comments
 (0)