The open source OpenXR runtime
1// Copyright 2019-2020, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Android window code.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
8 * @author Jakob Bornecrantz <jakob@collabora.com>
9 * @ingroup comp_main
10 */
11
12#include "xrt/xrt_compiler.h"
13
14#include "util/u_misc.h"
15
16#include "android/android_globals.h"
17#include "android/android_custom_surface.h"
18
19#include "main/comp_window.h"
20
21#include <android/native_window.h>
22
23#include <poll.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <linux/input.h>
28
29#define WINDOW_TITLE "Monado"
30
31/*
32 *
33 * Private structs.
34 *
35 */
36
37/*!
38 * An Android window.
39 *
40 * @implements comp_target_swapchain
41 */
42struct comp_window_android
43{
44 struct comp_target_swapchain base;
45
46 struct android_custom_surface *custom_surface;
47};
48
49
50/*
51 *
52 * Functions.
53 *
54 */
55
56static inline struct vk_bundle *
57get_vk(struct comp_window_android *cwa)
58{
59 return &cwa->base.base.c->base.vk;
60}
61
62static bool
63comp_window_android_init(struct comp_target *ct)
64{
65 (void)ct;
66
67 return true;
68}
69
70static void
71comp_window_android_destroy(struct comp_target *ct)
72{
73 struct comp_window_android *cwa = (struct comp_window_android *)ct;
74
75 comp_target_swapchain_cleanup(&cwa->base);
76
77 android_custom_surface_destroy(&cwa->custom_surface);
78
79 free(ct);
80}
81
82static void
83comp_window_android_update_window_title(struct comp_target *ct, const char *title)
84{
85 (void)ct;
86}
87
88static struct ANativeWindow *
89_create_android_window(struct comp_window_android *cwa)
90{
91 // 0 means default display
92 cwa->custom_surface = android_custom_surface_async_start( //
93 android_globals_get_vm(), // vm
94 android_globals_get_context(), // context
95 0, // display_id
96 WINDOW_TITLE, // title in dumpsys
97 0); // preferred_display_mode_id
98
99 if (cwa->custom_surface == NULL) {
100 COMP_ERROR(cwa->base.base.c,
101 "comp_window_android_create_surface: could not "
102 "start asynchronous attachment of our custom surface");
103 return NULL;
104 }
105
106 return android_custom_surface_wait_get_surface(cwa->custom_surface, 2000);
107}
108
109static VkResult
110comp_window_android_create_surface(struct comp_window_android *cwa,
111 struct ANativeWindow *window,
112 VkSurfaceKHR *out_surface)
113{
114 struct vk_bundle *vk = get_vk(cwa);
115 VkResult ret;
116
117 VkAndroidSurfaceCreateInfoKHR surface_info = {
118 .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
119 .flags = 0,
120 .window = window,
121 };
122
123 VkSurfaceKHR surface = VK_NULL_HANDLE;
124 ret = vk->vkCreateAndroidSurfaceKHR( //
125 vk->instance, //
126 &surface_info, //
127 NULL, //
128 &surface); //
129 if (ret != VK_SUCCESS) {
130 COMP_ERROR(cwa->base.base.c, "vkCreateAndroidSurfaceKHR: %s", vk_result_string(ret));
131 return ret;
132 }
133
134 VK_NAME_SURFACE(vk, surface, "comp_window_android surface");
135 *out_surface = surface;
136
137 return VK_SUCCESS;
138}
139
140static bool
141comp_window_android_init_swapchain(struct comp_target *ct, uint32_t width, uint32_t height)
142{
143 struct comp_window_android *cwa = (struct comp_window_android *)ct;
144 VkResult ret;
145
146 struct ANativeWindow *window = NULL;
147
148 if (android_globals_get_activity() != NULL) {
149 /* In process: Creating surface from activity */
150 window = _create_android_window(cwa);
151 } else if (android_custom_surface_can_draw_overlays(android_globals_get_vm(), android_globals_get_context())) {
152 /* Out of process: Create surface */
153 window = _create_android_window(cwa);
154 } else {
155 /* Out of process: Getting cached surface.
156 * This loop polls for a surface created by Client.java in blockingConnect.
157 * TODO: change java code to callback native code to notify Session lifecycle progress, instead
158 * of polling here
159 */
160 for (int i = 0; i < 100; i++) {
161 window = (struct ANativeWindow *)android_globals_get_window();
162 if (window)
163 break;
164 os_nanosleep(20 * U_TIME_1MS_IN_NS);
165 }
166 }
167
168
169 if (window == NULL) {
170 COMP_ERROR(cwa->base.base.c, "could not get ANativeWindow");
171 return false;
172 }
173 android_globals_store_window((struct _ANativeWindow *)window);
174
175 ret = comp_window_android_create_surface(cwa, window, &cwa->base.surface.handle);
176 if (ret != VK_SUCCESS) {
177 COMP_ERROR(ct->c, "Failed to create surface '%s'!", vk_result_string(ret));
178 return false;
179 }
180
181 return true;
182}
183
184static void
185comp_window_android_flush(struct comp_target *ct)
186{
187 (void)ct;
188}
189
190struct comp_target *
191comp_window_android_create(struct comp_compositor *c)
192{
193 struct comp_window_android *w = U_TYPED_CALLOC(struct comp_window_android);
194
195 // The display timing code hasn't been tested on Android and may be broken.
196 comp_target_swapchain_init_and_set_fnptrs(&w->base, COMP_TARGET_FORCE_FAKE_DISPLAY_TIMING);
197
198 w->base.base.name = "Android";
199 w->base.base.destroy = comp_window_android_destroy;
200 w->base.base.flush = comp_window_android_flush;
201 w->base.base.init_pre_vulkan = comp_window_android_init;
202 w->base.base.init_post_vulkan = comp_window_android_init_swapchain;
203 w->base.base.set_title = comp_window_android_update_window_title;
204 w->base.base.c = c;
205
206 return &w->base.base;
207}
208
209
210/*
211 *
212 * Factory
213 *
214 */
215
216static const char *instance_extensions[] = {
217 VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
218};
219
220static bool
221detect(const struct comp_target_factory *ctf, struct comp_compositor *c)
222{
223 return false;
224}
225
226static bool
227create_target(const struct comp_target_factory *ctf, struct comp_compositor *c, struct comp_target **out_ct)
228{
229 struct comp_target *ct = comp_window_android_create(c);
230 if (ct == NULL) {
231 return false;
232 }
233
234 *out_ct = ct;
235
236 return true;
237}
238
239const struct comp_target_factory comp_target_factory_android = {
240 .name = "Android",
241 .identifier = "android",
242 .requires_vulkan_for_create = false,
243 .is_deferred = true,
244 .required_instance_version = 0,
245 .required_instance_extensions = instance_extensions,
246 .required_instance_extension_count = ARRAY_SIZE(instance_extensions),
247 .optional_device_extensions = NULL,
248 .optional_device_extension_count = 0,
249 .detect = detect,
250 .create_target = create_target,
251};