|
@@ -63,6 +63,198 @@
|
|
|
#define OPENXR_LOADER_NAME "libopenxr_loader.so"
|
|
|
#endif
|
|
|
|
|
|
+////////////////////////////////////
|
|
|
+// OpenXRAPI::OpenXRSwapChainInfo
|
|
|
+
|
|
|
+Vector<OpenXRAPI::OpenXRSwapChainInfo> OpenXRAPI::OpenXRSwapChainInfo::free_queue;
|
|
|
+
|
|
|
+bool OpenXRAPI::OpenXRSwapChainInfo::create(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size) {
|
|
|
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
|
+ ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
+
|
|
|
+ XrSession xr_session = openxr_api->get_session();
|
|
|
+ ERR_FAIL_COND_V(xr_session == XR_NULL_HANDLE, false);
|
|
|
+
|
|
|
+ OpenXRGraphicsExtensionWrapper *xr_graphics_extension = openxr_api->get_graphics_extension();
|
|
|
+ ERR_FAIL_NULL_V(xr_graphics_extension, false);
|
|
|
+
|
|
|
+ // We already have a swapchain?
|
|
|
+ ERR_FAIL_COND_V(swapchain != XR_NULL_HANDLE, false);
|
|
|
+
|
|
|
+ XrResult result;
|
|
|
+
|
|
|
+ void *next_pointer = nullptr;
|
|
|
+ for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) {
|
|
|
+ void *np = wrapper->set_swapchain_create_info_and_get_next_pointer(next_pointer);
|
|
|
+ if (np != nullptr) {
|
|
|
+ next_pointer = np;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XrSwapchainCreateInfo swapchain_create_info = {
|
|
|
+ XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
|
|
|
+ next_pointer, // next
|
|
|
+ p_create_flags, // createFlags
|
|
|
+ p_usage_flags, // usageFlags
|
|
|
+ p_swapchain_format, // format
|
|
|
+ p_sample_count, // sampleCount
|
|
|
+ p_width, // width
|
|
|
+ p_height, // height
|
|
|
+ 1, // faceCount
|
|
|
+ p_array_size, // arraySize
|
|
|
+ 1 // mipCount
|
|
|
+ };
|
|
|
+
|
|
|
+ XrSwapchain new_swapchain;
|
|
|
+ result = openxr_api->xrCreateSwapchain(xr_session, &swapchain_create_info, &new_swapchain);
|
|
|
+ if (XR_FAILED(result)) {
|
|
|
+ print_line("OpenXR: Failed to get swapchain [", openxr_api->get_error_string(result), "]");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!xr_graphics_extension->get_swapchain_image_data(new_swapchain, p_swapchain_format, p_width, p_height, p_sample_count, p_array_size, &swapchain_graphics_data)) {
|
|
|
+ openxr_api->xrDestroySwapchain(new_swapchain);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ swapchain = new_swapchain;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void OpenXRAPI::OpenXRSwapChainInfo::queue_free() {
|
|
|
+ if (image_acquired) {
|
|
|
+ release();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (swapchain != XR_NULL_HANDLE) {
|
|
|
+ free_queue.push_back(*this);
|
|
|
+
|
|
|
+ swapchain_graphics_data = nullptr;
|
|
|
+ swapchain = XR_NULL_HANDLE;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void OpenXRAPI::OpenXRSwapChainInfo::free_queued() {
|
|
|
+ for (OpenXRAPI::OpenXRSwapChainInfo &swapchain_info : free_queue) {
|
|
|
+ swapchain_info.free();
|
|
|
+ }
|
|
|
+ free_queue.clear();
|
|
|
+}
|
|
|
+
|
|
|
+void OpenXRAPI::OpenXRSwapChainInfo::free() {
|
|
|
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
|
+ ERR_FAIL_NULL(openxr_api);
|
|
|
+
|
|
|
+ if (image_acquired) {
|
|
|
+ release();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (openxr_api->get_graphics_extension() && swapchain_graphics_data != nullptr) {
|
|
|
+ openxr_api->get_graphics_extension()->cleanup_swapchain_graphics_data(&swapchain_graphics_data);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (swapchain != XR_NULL_HANDLE) {
|
|
|
+ openxr_api->xrDestroySwapchain(swapchain);
|
|
|
+ swapchain = XR_NULL_HANDLE;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool OpenXRAPI::OpenXRSwapChainInfo::acquire(XrBool32 &p_should_render) {
|
|
|
+ ERR_FAIL_COND_V(image_acquired, true); // This was not released when it should be, error out and reuse...
|
|
|
+
|
|
|
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
|
+ ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
+
|
|
|
+ XrResult result;
|
|
|
+
|
|
|
+ if (!skip_acquire_swapchain) {
|
|
|
+ XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
|
|
|
+ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type
|
|
|
+ nullptr // next
|
|
|
+ };
|
|
|
+
|
|
|
+ result = openxr_api->xrAcquireSwapchainImage(swapchain, &swapchain_image_acquire_info, &image_index);
|
|
|
+ if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
|
|
+ // Make sure end_frame knows we need to submit an empty frame
|
|
|
+ p_should_render = false;
|
|
|
+
|
|
|
+ if (XR_FAILED(result)) {
|
|
|
+ // Unexpected failure, log this!
|
|
|
+ print_line("OpenXR: failed to acquire swapchain image [", openxr_api->get_error_string(result), "]");
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ // In this scenario we silently fail, the XR runtime is simply not ready yet to acquire the swapchain.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XrSwapchainImageWaitInfo swapchain_image_wait_info = {
|
|
|
+ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
|
|
|
+ nullptr, // next
|
|
|
+ 17000000 // timeout in nanoseconds
|
|
|
+ };
|
|
|
+
|
|
|
+ result = openxr_api->xrWaitSwapchainImage(swapchain, &swapchain_image_wait_info);
|
|
|
+ if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
|
|
+ // Make sure end_frame knows we need to submit an empty frame
|
|
|
+ p_should_render = false;
|
|
|
+
|
|
|
+ if (XR_FAILED(result)) {
|
|
|
+ // Unexpected failure, log this!
|
|
|
+ print_line("OpenXR: failed to wait for swapchain image [", openxr_api->get_error_string(result), "]");
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ // Make sure to skip trying to acquire the swapchain image in the next frame
|
|
|
+ skip_acquire_swapchain = true;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ skip_acquire_swapchain = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ image_acquired = true;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool OpenXRAPI::OpenXRSwapChainInfo::release() {
|
|
|
+ if (!image_acquired) {
|
|
|
+ // Already released or never acquired.
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ image_acquired = false; // Regardless if we succeed or not, consider this released.
|
|
|
+
|
|
|
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
|
+ ERR_FAIL_NULL_V(openxr_api, false);
|
|
|
+
|
|
|
+ XrSwapchainImageReleaseInfo swapchain_image_release_info = {
|
|
|
+ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
|
|
|
+ nullptr // next
|
|
|
+ };
|
|
|
+ XrResult result = openxr_api->xrReleaseSwapchainImage(swapchain, &swapchain_image_release_info);
|
|
|
+ if (XR_FAILED(result)) {
|
|
|
+ print_line("OpenXR: failed to release swapchain image! [", openxr_api->get_error_string(result), "]");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+RID OpenXRAPI::OpenXRSwapChainInfo::get_image() {
|
|
|
+ OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
|
|
|
+
|
|
|
+ if (image_acquired && openxr_api && openxr_api->get_graphics_extension()) {
|
|
|
+ return OpenXRAPI::get_singleton()->get_graphics_extension()->get_texture(swapchain_graphics_data, image_index);
|
|
|
+ } else {
|
|
|
+ return RID();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////
|
|
|
+// OpenXRAPI
|
|
|
+
|
|
|
OpenXRAPI *OpenXRAPI::singleton = nullptr;
|
|
|
Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers;
|
|
|
|
|
@@ -568,6 +760,21 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType
|
|
|
print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount));
|
|
|
}
|
|
|
|
|
|
+ // Allocate buffers we'll be populating with view information.
|
|
|
+ views = (XrView *)memalloc(sizeof(XrView) * view_count);
|
|
|
+ ERR_FAIL_NULL_V_MSG(views, false, "OpenXR Couldn't allocate memory for views");
|
|
|
+ memset(views, 0, sizeof(XrView) * view_count);
|
|
|
+
|
|
|
+ projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
|
|
|
+ ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
|
|
|
+ memset(projection_views, 0, sizeof(XrCompositionLayerProjectionView) * view_count);
|
|
|
+
|
|
|
+ if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
|
|
+ depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
|
|
|
+ ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
|
|
|
+ memset(depth_views, 0, sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
|
|
|
+ }
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -878,31 +1085,10 @@ bool OpenXRAPI::is_swapchain_format_supported(int64_t p_swapchain_format) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-bool OpenXRAPI::create_swapchains() {
|
|
|
+bool OpenXRAPI::obtain_swapchain_formats() {
|
|
|
ERR_FAIL_NULL_V(graphics_extension, false);
|
|
|
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
|
|
|
|
|
|
- /*
|
|
|
- TODO: We need to improve on this, for now we're taking our old approach of creating our main swapchains and substituting
|
|
|
- those for the ones Godot normally creates.
|
|
|
- This however means we can only use swapchains for our main XR view.
|
|
|
-
|
|
|
- It would have been nicer if we could override the swapchain creation in Godot with ours but we have a timing issue here.
|
|
|
- We can't create XR swapchains until after our XR session is fully instantiated, yet Godot creates its swapchain much earlier.
|
|
|
-
|
|
|
- Also Godot only creates a swapchain for the main output.
|
|
|
- OpenXR will require us to create swapchains as the render target for additional viewports if we want to use the layer system
|
|
|
- to optimize text rendering and background rendering as OpenXR may choose to reuse the results for reprojection while we're
|
|
|
- already rendering the next frame.
|
|
|
-
|
|
|
- Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
|
|
|
- as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
|
|
|
- */
|
|
|
-
|
|
|
- Size2 recommended_size = get_recommended_target_size();
|
|
|
- uint32_t sample_count = 1;
|
|
|
-
|
|
|
- // We start with our color swapchain...
|
|
|
{
|
|
|
// Build a vector with swapchain formats we want to use, from best fit to worst
|
|
|
Vector<int64_t> usable_swapchain_formats;
|
|
@@ -923,23 +1109,9 @@ bool OpenXRAPI::create_swapchains() {
|
|
|
} else {
|
|
|
print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(color_swapchain_format));
|
|
|
}
|
|
|
-
|
|
|
- if (!create_swapchain(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- views = (XrView *)memalloc(sizeof(XrView) * view_count);
|
|
|
- ERR_FAIL_NULL_V_MSG(views, false, "OpenXR Couldn't allocate memory for views");
|
|
|
-
|
|
|
- projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
|
|
|
- ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
|
|
|
-
|
|
|
- // We create our depth swapchain if:
|
|
|
- // - we've enabled submitting depth buffer
|
|
|
- // - we support our depth layer extension
|
|
|
- // - we have our spacewarp extension (not yet implemented)
|
|
|
- if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
|
|
+ {
|
|
|
// Build a vector with swapchain formats we want to use, from best fit to worst
|
|
|
Vector<int64_t> usable_swapchain_formats;
|
|
|
depth_swapchain_format = 0;
|
|
@@ -954,18 +1126,51 @@ bool OpenXRAPI::create_swapchains() {
|
|
|
}
|
|
|
|
|
|
if (depth_swapchain_format == 0) {
|
|
|
- print_line("Couldn't find usable depth swap chain format, depth buffer will not be submitted.");
|
|
|
+ WARN_PRINT_ONCE("Couldn't find usable depth swap chain format, depth buffer will not be submitted if requested.");
|
|
|
} else {
|
|
|
print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(depth_swapchain_format));
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // Note, if VK_FORMAT_D32_SFLOAT is used here but we're using the forward+ renderer, we should probably output a warning.
|
|
|
+ return true;
|
|
|
+}
|
|
|
|
|
|
- if (!create_swapchain(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
|
|
|
+ ERR_FAIL_NULL_V(graphics_extension, false);
|
|
|
+ ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
|
|
|
|
|
|
- depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
|
|
|
- ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
|
|
|
+ /*
|
|
|
+ TODO: We need to improve on this, for now we're taking our old approach of creating our main swapchains and substituting
|
|
|
+ those for the ones Godot normally creates.
|
|
|
+ This however means we can only use swapchains for our main XR view.
|
|
|
+
|
|
|
+ It would have been nicer if we could override the swapchain creation in Godot with ours but we have a timing issue here.
|
|
|
+ We can't create XR swapchains until after our XR session is fully instantiated, yet Godot creates its swapchain much earlier.
|
|
|
+
|
|
|
+ We only creates a swapchain for the main output here.
|
|
|
+ Additional swapchains may be created through our composition layer extension.
|
|
|
+
|
|
|
+ Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
|
|
|
+ as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
|
|
|
+ */
|
|
|
+
|
|
|
+ main_swapchain_size = p_size;
|
|
|
+ uint32_t sample_count = 1;
|
|
|
+
|
|
|
+ // We start with our color swapchain...
|
|
|
+ if (color_swapchain_format != 0) {
|
|
|
+ if (!main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // We create our depth swapchain if:
|
|
|
+ // - we've enabled submitting depth buffer
|
|
|
+ // - we support our depth layer extension
|
|
|
+ // - we have our spacewarp extension (not yet implemented)
|
|
|
+ if (depth_swapchain_format != 0 && submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
|
|
|
+ if (!main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, main_swapchain_size.width, main_swapchain_size.height, sample_count, view_count)) {
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -981,24 +1186,24 @@ bool OpenXRAPI::create_swapchains() {
|
|
|
|
|
|
projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
|
|
projection_views[i].next = nullptr;
|
|
|
- projection_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain;
|
|
|
+ projection_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
|
|
projection_views[i].subImage.imageArrayIndex = i;
|
|
|
projection_views[i].subImage.imageRect.offset.x = 0;
|
|
|
projection_views[i].subImage.imageRect.offset.y = 0;
|
|
|
- projection_views[i].subImage.imageRect.extent.width = recommended_size.width;
|
|
|
- projection_views[i].subImage.imageRect.extent.height = recommended_size.height;
|
|
|
+ projection_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
|
|
|
+ projection_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
|
|
|
|
|
|
if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
|
|
|
projection_views[i].next = &depth_views[i];
|
|
|
|
|
|
depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
|
|
|
depth_views[i].next = nullptr;
|
|
|
- depth_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain;
|
|
|
+ depth_views[i].subImage.swapchain = main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_swapchain();
|
|
|
depth_views[i].subImage.imageArrayIndex = i;
|
|
|
depth_views[i].subImage.imageRect.offset.x = 0;
|
|
|
depth_views[i].subImage.imageRect.offset.y = 0;
|
|
|
- depth_views[i].subImage.imageRect.extent.width = recommended_size.width;
|
|
|
- depth_views[i].subImage.imageRect.extent.height = recommended_size.height;
|
|
|
+ depth_views[i].subImage.imageRect.extent.width = main_swapchain_size.width;
|
|
|
+ depth_views[i].subImage.imageRect.extent.height = main_swapchain_size.height;
|
|
|
depth_views[i].minDepth = 0.0;
|
|
|
depth_views[i].maxDepth = 1.0;
|
|
|
depth_views[i].nearZ = 0.01; // Near and far Z will be set to the correct values in fill_projection_matrix
|
|
@@ -1029,9 +1234,8 @@ void OpenXRAPI::destroy_session() {
|
|
|
depth_views = nullptr;
|
|
|
}
|
|
|
|
|
|
- for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
|
|
- free_swapchain(swapchains[i]);
|
|
|
- }
|
|
|
+ free_main_swapchains();
|
|
|
+ OpenXRSwapChainInfo::free_queued();
|
|
|
|
|
|
if (supported_swapchain_formats != nullptr) {
|
|
|
memfree(supported_swapchain_formats);
|
|
@@ -1064,51 +1268,6 @@ void OpenXRAPI::destroy_session() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-bool OpenXRAPI::create_swapchain(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
|
|
|
- ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
|
|
|
- ERR_FAIL_NULL_V(graphics_extension, false);
|
|
|
-
|
|
|
- XrResult result;
|
|
|
-
|
|
|
- void *next_pointer = nullptr;
|
|
|
- for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
|
|
- void *np = wrapper->set_swapchain_create_info_and_get_next_pointer(next_pointer);
|
|
|
- if (np != nullptr) {
|
|
|
- next_pointer = np;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- XrSwapchainCreateInfo swapchain_create_info = {
|
|
|
- XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
|
|
|
- next_pointer, // next
|
|
|
- p_create_flags, // createFlags
|
|
|
- p_usage_flags, // usageFlags
|
|
|
- p_swapchain_format, // format
|
|
|
- p_sample_count, // sampleCount
|
|
|
- p_width, // width
|
|
|
- p_height, // height
|
|
|
- 1, // faceCount
|
|
|
- p_array_size, // arraySize
|
|
|
- 1 // mipCount
|
|
|
- };
|
|
|
-
|
|
|
- XrSwapchain new_swapchain;
|
|
|
- result = xrCreateSwapchain(session, &swapchain_create_info, &new_swapchain);
|
|
|
- if (XR_FAILED(result)) {
|
|
|
- print_line("OpenXR: Failed to get swapchain [", get_error_string(result), "]");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!graphics_extension->get_swapchain_image_data(new_swapchain, p_swapchain_format, p_width, p_height, p_sample_count, p_array_size, r_swapchain_graphics_data)) {
|
|
|
- xrDestroySwapchain(new_swapchain);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- r_swapchain = new_swapchain;
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
bool OpenXRAPI::on_state_idle() {
|
|
|
print_verbose("On state idle");
|
|
|
|
|
@@ -1135,17 +1294,6 @@ bool OpenXRAPI::on_state_ready() {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- // This is when we create our swapchain, this can be a "long" time after Godot finishes, we can deal with this for now
|
|
|
- // but once we want to provide Viewports for additional layers where OpenXR requires us to create further swapchains,
|
|
|
- // we'll be creating those viewport WAY before we reach this point.
|
|
|
- // We may need to implement a wait in our init in main.cpp polling our events until the session is ready.
|
|
|
- // That will be very very ugly
|
|
|
- // The other possibility is to create a separate OpenXRViewport type specifically for this goal as part of our OpenXR module
|
|
|
-
|
|
|
- if (!create_swapchains()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
// we're running
|
|
|
running = true;
|
|
|
|
|
@@ -1157,8 +1305,6 @@ bool OpenXRAPI::on_state_ready() {
|
|
|
xr_interface->on_state_ready();
|
|
|
}
|
|
|
|
|
|
- // TODO Tell android
|
|
|
-
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -1492,6 +1638,11 @@ bool OpenXRAPI::initialize_session() {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ if (!obtain_swapchain_formats()) {
|
|
|
+ destroy_session();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -1798,103 +1949,10 @@ bool OpenXRAPI::process() {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-void OpenXRAPI::free_swapchain(OpenXRSwapChainInfo &p_swapchain) {
|
|
|
- if (p_swapchain.image_acquired) {
|
|
|
- release_image(p_swapchain);
|
|
|
- }
|
|
|
-
|
|
|
- if (graphics_extension && p_swapchain.swapchain_graphics_data != nullptr) {
|
|
|
- graphics_extension->cleanup_swapchain_graphics_data(&p_swapchain.swapchain_graphics_data);
|
|
|
- }
|
|
|
-
|
|
|
- if (p_swapchain.swapchain != XR_NULL_HANDLE) {
|
|
|
- xrDestroySwapchain(p_swapchain.swapchain);
|
|
|
- p_swapchain.swapchain = XR_NULL_HANDLE;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
|
|
|
- ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // This was not released when it should be, error out and reuse...
|
|
|
-
|
|
|
- XrResult result;
|
|
|
-
|
|
|
- if (!p_swapchain.skip_acquire_swapchain) {
|
|
|
- XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
|
|
|
- XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type
|
|
|
- nullptr // next
|
|
|
- };
|
|
|
-
|
|
|
- result = xrAcquireSwapchainImage(p_swapchain.swapchain, &swapchain_image_acquire_info, &p_swapchain.image_index);
|
|
|
- if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
|
|
- // Make sure end_frame knows we need to submit an empty frame
|
|
|
- frame_state.shouldRender = false;
|
|
|
-
|
|
|
- if (XR_FAILED(result)) {
|
|
|
- // Unexpected failure, log this!
|
|
|
- print_line("OpenXR: failed to acquire swapchain image [", get_error_string(result), "]");
|
|
|
- return false;
|
|
|
- } else {
|
|
|
- // In this scenario we silently fail, the XR runtime is simply not ready yet to acquire the swapchain.
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- XrSwapchainImageWaitInfo swapchain_image_wait_info = {
|
|
|
- XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, // type
|
|
|
- nullptr, // next
|
|
|
- 17000000 // timeout in nanoseconds
|
|
|
- };
|
|
|
-
|
|
|
- result = xrWaitSwapchainImage(p_swapchain.swapchain, &swapchain_image_wait_info);
|
|
|
- if (!XR_UNQUALIFIED_SUCCESS(result)) {
|
|
|
- // Make sure end_frame knows we need to submit an empty frame
|
|
|
- frame_state.shouldRender = false;
|
|
|
-
|
|
|
- if (XR_FAILED(result)) {
|
|
|
- // Unexpected failure, log this!
|
|
|
- print_line("OpenXR: failed to wait for swapchain image [", get_error_string(result), "]");
|
|
|
- return false;
|
|
|
- } else {
|
|
|
- // Make sure to skip trying to acquire the swapchain image in the next frame
|
|
|
- p_swapchain.skip_acquire_swapchain = true;
|
|
|
- return false;
|
|
|
- }
|
|
|
- } else {
|
|
|
- p_swapchain.skip_acquire_swapchain = false;
|
|
|
- }
|
|
|
-
|
|
|
- p_swapchain.image_acquired = true;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-RID OpenXRAPI::get_image(OpenXRSwapChainInfo &p_swapchain) {
|
|
|
- if (p_swapchain.image_acquired) {
|
|
|
- return graphics_extension->get_texture(p_swapchain.swapchain_graphics_data, p_swapchain.image_index);
|
|
|
- } else {
|
|
|
- return RID();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool OpenXRAPI::release_image(OpenXRSwapChainInfo &p_swapchain) {
|
|
|
- if (!p_swapchain.image_acquired) {
|
|
|
- // Already released or never acquired.
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- p_swapchain.image_acquired = false; // Regardless if we succeed or not, consider this released.
|
|
|
-
|
|
|
- XrSwapchainImageReleaseInfo swapchain_image_release_info = {
|
|
|
- XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
|
|
|
- nullptr // next
|
|
|
- };
|
|
|
- XrResult result = xrReleaseSwapchainImage(p_swapchain.swapchain, &swapchain_image_release_info);
|
|
|
- if (XR_FAILED(result)) {
|
|
|
- print_line("OpenXR: failed to release swapchain image! [", get_error_string(result), "]");
|
|
|
- return false;
|
|
|
+void OpenXRAPI::free_main_swapchains() {
|
|
|
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
|
|
+ main_swapchains[i].queue_free();
|
|
|
}
|
|
|
-
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
void OpenXRAPI::pre_render() {
|
|
@@ -1904,6 +1962,18 @@ void OpenXRAPI::pre_render() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // Process any swapchains that were queued to be freed
|
|
|
+ OpenXRSwapChainInfo::free_queued();
|
|
|
+
|
|
|
+ Size2i swapchain_size = get_recommended_target_size();
|
|
|
+ if (swapchain_size != main_swapchain_size) {
|
|
|
+ // Out with the old.
|
|
|
+ free_main_swapchains();
|
|
|
+
|
|
|
+ // In with the new.
|
|
|
+ create_main_swapchains(swapchain_size);
|
|
|
+ }
|
|
|
+
|
|
|
// Waitframe does 2 important things in our process:
|
|
|
// 1) It provides us with predictive timing, telling us when OpenXR expects to display the frame we're about to commit
|
|
|
// 2) It will use the previous timing to pause our thread so that rendering starts as close to displaying as possible
|
|
@@ -1996,9 +2066,15 @@ void OpenXRAPI::pre_render() {
|
|
|
print_line("OpenXR: failed to being frame [", get_error_string(result), "]");
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ // Reset this, we haven't found a viewport for output yet
|
|
|
+ has_xr_viewport = false;
|
|
|
}
|
|
|
|
|
|
bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
|
|
+ // We found an XR viewport!
|
|
|
+ has_xr_viewport = true;
|
|
|
+
|
|
|
if (!can_render()) {
|
|
|
return false;
|
|
|
}
|
|
@@ -2007,8 +2083,8 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
|
|
|
|
|
// Acquire our images
|
|
|
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
|
|
- if (!swapchains[i].image_acquired && swapchains[i].swapchain != XR_NULL_HANDLE) {
|
|
|
- if (!acquire_image(swapchains[i])) {
|
|
|
+ if (!main_swapchains[i].is_image_acquired() && main_swapchains[i].get_swapchain() != XR_NULL_HANDLE) {
|
|
|
+ if (!main_swapchains[i].acquire(frame_state.shouldRender)) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
@@ -2022,17 +2098,17 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
|
|
}
|
|
|
|
|
|
XrSwapchain OpenXRAPI::get_color_swapchain() {
|
|
|
- return swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain;
|
|
|
+ return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain();
|
|
|
}
|
|
|
|
|
|
RID OpenXRAPI::get_color_texture() {
|
|
|
- return get_image(swapchains[OPENXR_SWAPCHAIN_COLOR]);
|
|
|
+ return main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_image();
|
|
|
}
|
|
|
|
|
|
RID OpenXRAPI::get_depth_texture() {
|
|
|
// Note, image will not be acquired if we didn't have a suitable swap chain format.
|
|
|
if (submit_depth_buffer) {
|
|
|
- return get_image(swapchains[OPENXR_SWAPCHAIN_DEPTH]);
|
|
|
+ return main_swapchains[OPENXR_SWAPCHAIN_DEPTH].get_image();
|
|
|
} else {
|
|
|
return RID();
|
|
|
}
|
|
@@ -2057,15 +2133,19 @@ void OpenXRAPI::end_frame() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (frame_state.shouldRender && view_pose_valid && !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
|
|
|
- print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
|
|
|
+ if (frame_state.shouldRender && view_pose_valid) {
|
|
|
+ if (!has_xr_viewport) {
|
|
|
+ print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
|
|
|
+ } else if (!main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
|
|
+ print_line("OpenXR: No swapchain could be acquired to render to!");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// must have:
|
|
|
// - shouldRender set to true
|
|
|
// - a valid view pose for projection_views[eye].pose to submit layer
|
|
|
// - an image to render
|
|
|
- if (!frame_state.shouldRender || !view_pose_valid || !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
|
|
|
+ if (!frame_state.shouldRender || !view_pose_valid || !main_swapchains[OPENXR_SWAPCHAIN_COLOR].is_image_acquired()) {
|
|
|
// submit 0 layers when we shouldn't render
|
|
|
XrFrameEndInfo frame_end_info = {
|
|
|
XR_TYPE_FRAME_END_INFO, // type
|
|
@@ -2087,8 +2167,8 @@ void OpenXRAPI::end_frame() {
|
|
|
|
|
|
// release our swapchain image if we acquired it
|
|
|
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
|
|
|
- if (swapchains[i].image_acquired) {
|
|
|
- release_image(swapchains[i]);
|
|
|
+ if (main_swapchains[i].is_image_acquired()) {
|
|
|
+ main_swapchains[i].release();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2332,7 +2412,7 @@ OpenXRAPI::OpenXRAPI() {
|
|
|
submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
|
|
|
}
|
|
|
|
|
|
- // reset a few things that can't be done in our class definition
|
|
|
+ // Reset a few things that can't be done in our class definition.
|
|
|
frame_state.predictedDisplayTime = 0;
|
|
|
frame_state.predictedDisplayPeriod = 0;
|
|
|
}
|