Skip to content

Commit

Permalink
Implement XR_FB_space_warp extension
Browse files Browse the repository at this point in the history
  • Loading branch information
devloglogan committed Nov 5, 2024
1 parent dbc2180 commit ae73152
Show file tree
Hide file tree
Showing 26 changed files with 11,415 additions and 209 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
/**************************************************************************/
/* openxr_fb_space_warp_extension_wrapper.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT XR */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "extensions/openxr_fb_space_warp_extension_wrapper.h"

#include <godot_cpp/classes/open_xr_interface.hpp>
#include <godot_cpp/classes/open_xrapi_extension.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/xr_server.hpp>
#include <godot_cpp/variant/utility_functions.hpp>

#define GL_RGBA16F 0x881A
#define GL_DEPTH24_STENCIL8 0x88F0

OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::singleton = nullptr;

OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::get_singleton() {
if (singleton == nullptr) {
singleton = memnew(OpenXRFbSpaceWarpExtensionWrapper());
}
return singleton;
}

OpenXRFbSpaceWarpExtensionWrapper::OpenXRFbSpaceWarpExtensionWrapper() :
OpenXRExtensionWrapperExtension() {
ERR_FAIL_COND_MSG(singleton != nullptr, "An OpenXRFbSpaceWarpExtensionWrapper singleton already exists.");

request_extensions[XR_FB_SPACE_WARP_EXTENSION_NAME] = &fb_space_warp_ext;
singleton = this;
}

OpenXRFbSpaceWarpExtensionWrapper::~OpenXRFbSpaceWarpExtensionWrapper() {
singleton = nullptr;
}

godot::Dictionary OpenXRFbSpaceWarpExtensionWrapper::_get_requested_extensions() {
godot::Dictionary result;
for (auto ext : request_extensions) {
godot::String key = ext.first;
uint64_t value = reinterpret_cast<uint64_t>(ext.second);
result[key] = (godot::Variant)value;
}
return result;
}

uint64_t OpenXRFbSpaceWarpExtensionWrapper::_set_system_properties_and_get_next_pointer(void *p_next_pointer) {
system_space_warp_properties.next = p_next_pointer;
return reinterpret_cast<uint64_t>(&system_space_warp_properties);
}

uint64_t OpenXRFbSpaceWarpExtensionWrapper::_set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) {
if (fb_space_warp_ext && enabled) {
space_warp_info[p_view_index].next = p_next_pointer;
return reinterpret_cast<uint64_t>(&space_warp_info[p_view_index]);
} else {
return reinterpret_cast<uint64_t>(p_next_pointer);
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_session_created(uint64_t p_instance) {
if (!fb_space_warp_ext) {
return;
}

String graphics_api_name = get_openxr_api()->get_graphics_api_name();
if (graphics_api_name != "OpenGL") {
UtilityFunctions::print("Disabling XR_FB_space_warp extension; this extension is only implmented for OpenGL");
fb_space_warp_ext = false;
return;
}

get_openxr_api()->register_projection_views_extension(this);
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_session_destroyed() {
if (fb_space_warp_ext) {
get_openxr_api()->unregister_projection_views_extension(this);
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_state_ready() {
ProjectSettings *project_settings = ProjectSettings::get_singleton();
bool is_project_setting_enabled = (bool)project_settings->get_setting_with_override("xr/openxr/extensions/application_space_warp");
if (!is_project_setting_enabled) {
fb_space_warp_ext = false;
} else {
enabled = true;
}

if (!fb_space_warp_ext || !enabled) {
return;
}

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();
int width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
int height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;

String graphics_api_name = get_openxr_api()->get_graphics_api_name();
if (graphics_api_name == "OpenGL") {
motion_vector_swapchain_info = get_openxr_api()->openxr_swapchain_create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, GL_RGBA16F, width, height, 1, view_count);
motion_vector_depth_swapchain_info = get_openxr_api()->openxr_swapchain_create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, GL_DEPTH24_STENCIL8, width, height, 1, view_count);
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_main_swapchains_created() {
if (!fb_space_warp_ext) {
return;
}

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();

space_warp_info = memnew_arr(XrCompositionLayerSpaceWarpInfoFB, 2);
for (int i = 0; i < view_count; i++) {
space_warp_info[i].type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB;

space_warp_info[i].next = nullptr;

space_warp_info[i].layerFlags = 0;

space_warp_info[i].motionVectorSubImage.swapchain = (XrSwapchain)get_openxr_api()->openxr_swapchain_get_swapchain(motion_vector_swapchain_info);
space_warp_info[i].motionVectorSubImage.imageRect.offset.x = 0;
space_warp_info[i].motionVectorSubImage.imageRect.offset.y = 0;
space_warp_info[i].motionVectorSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
space_warp_info[i].motionVectorSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
space_warp_info[i].motionVectorSubImage.imageArrayIndex = i;

space_warp_info[i].appSpaceDeltaPose = { { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } };

space_warp_info[i].depthSubImage.swapchain = (XrSwapchain)get_openxr_api()->openxr_swapchain_get_swapchain(motion_vector_depth_swapchain_info);
space_warp_info[i].depthSubImage.imageRect.offset.x = 0;
space_warp_info[i].depthSubImage.imageRect.offset.y = 0;
space_warp_info[i].depthSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
space_warp_info[i].depthSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
space_warp_info[i].depthSubImage.imageArrayIndex = i;

space_warp_info[i].minDepth = 0.0;
space_warp_info[i].maxDepth = 1.0;

space_warp_info[i].farZ = get_openxr_api()->get_render_state_z_near();
space_warp_info[i].nearZ = get_openxr_api()->get_render_state_z_far();
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_pre_render() {
if (!fb_space_warp_ext || !enabled) {
return;
}

get_openxr_api()->openxr_swapchain_acquire(motion_vector_swapchain_info);
get_openxr_api()->openxr_swapchain_acquire(motion_vector_depth_swapchain_info);

RID motion_vector_swapchain_image = get_openxr_api()->openxr_swapchain_get_image(motion_vector_swapchain_info);
get_openxr_api()->set_velocity_texture(motion_vector_swapchain_image);
RID motion_vector_depth_swapchain_image = get_openxr_api()->openxr_swapchain_get_image(motion_vector_depth_swapchain_info);
get_openxr_api()->set_velocity_depth_texture(motion_vector_depth_swapchain_image);

int target_width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
int target_height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
Size2i render_target_size = { target_width, target_height };
get_openxr_api()->set_velocity_target_size(render_target_size);

Transform3D world_transform = XRServer::get_singleton()->get_world_origin();
Transform3D delta_transform = render_state.previous_transform.affine_inverse() * world_transform;
Quaternion delta_quat = delta_transform.basis.get_quaternion();
Vector3 delta_origin = delta_transform.origin;

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();
for (int i = 0; i < view_count; i++) {
space_warp_info[i].layerFlags = render_state.skip_space_warp_frame ? XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB : 0;
space_warp_info[i].appSpaceDeltaPose = { { delta_quat.x, delta_quat.y, delta_quat.z, delta_quat.w }, { delta_origin.x, delta_origin.y, delta_origin.z } };
space_warp_info[i].farZ = get_openxr_api()->get_render_state_z_near();
space_warp_info[i].nearZ = get_openxr_api()->get_render_state_z_far();
}

render_state.skip_space_warp_frame = false;
render_state.previous_transform = world_transform;
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_post_draw_viewport(const RID &p_render_target) {
if (!fb_space_warp_ext || !enabled) {
return;
}

get_openxr_api()->openxr_swapchain_release(motion_vector_swapchain_info);
get_openxr_api()->openxr_swapchain_release(motion_vector_depth_swapchain_info);
}

bool OpenXRFbSpaceWarpExtensionWrapper::is_enabled() {
return fb_space_warp_ext && enabled;
}

void OpenXRFbSpaceWarpExtensionWrapper::set_space_warp_enabled(bool p_enable) {
enabled = p_enable;
}

void OpenXRFbSpaceWarpExtensionWrapper::skip_space_warp_frame() {
if (!fb_space_warp_ext || !enabled) {
return;
}

render_state.skip_space_warp_frame = true;
}

void OpenXRFbSpaceWarpExtensionWrapper::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_space_warp_enabled", "enable"), &OpenXRFbSpaceWarpExtensionWrapper::set_space_warp_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &OpenXRFbSpaceWarpExtensionWrapper::is_enabled);
ClassDB::bind_method(D_METHOD("skip_space_warp_frame"), &OpenXRFbSpaceWarpExtensionWrapper::skip_space_warp_frame);
}

void OpenXRFbSpaceWarpExtensionWrapper::add_project_setting() {
String p_name = "xr/openxr/extensions/application_space_warp";
if (!ProjectSettings::get_singleton()->has_setting(p_name)) {
ProjectSettings::get_singleton()->set_setting(p_name, false);
}

ProjectSettings::get_singleton()->set_initial_value(p_name, false);
ProjectSettings::get_singleton()->set_as_basic(p_name, true);
Dictionary property_info;
property_info["name"] = p_name;
property_info["type"] = Variant::Type::BOOL;
property_info["hint"] = PROPERTY_HINT_NONE;
ProjectSettings::get_singleton()->add_property_info(property_info);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**************************************************************************/
/* openxr_fb_space_warp_extension_wrapper.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT XR */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#ifndef OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H
#define OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H

#include <openxr/openxr.h>
#include <godot_cpp/classes/open_xr_extension_wrapper_extension.hpp>
#include <godot_cpp/classes/open_xrapi_extension.hpp>
#include <map>

using namespace godot;

class OpenXRFbSpaceWarpExtensionWrapper : public OpenXRExtensionWrapperExtension {
GDCLASS(OpenXRFbSpaceWarpExtensionWrapper, OpenXRExtensionWrapperExtension);

public:
static OpenXRFbSpaceWarpExtensionWrapper *get_singleton();

OpenXRFbSpaceWarpExtensionWrapper();
virtual ~OpenXRFbSpaceWarpExtensionWrapper() override;

godot::Dictionary _get_requested_extensions() override;

uint64_t _set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
uint64_t _set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) override;

void _on_session_created(uint64_t p_instance) override;
void _on_session_destroyed() override;
void _on_state_ready() override;
void _on_main_swapchains_created() override;
void _on_pre_render() override;
void _on_post_draw_viewport(const RID &p_render_target) override;

bool is_enabled();

void set_space_warp_enabled(bool p_enable);

void skip_space_warp_frame();

void add_project_setting();

protected:
static void _bind_methods();

private:
static OpenXRFbSpaceWarpExtensionWrapper *singleton;

bool enabled = false;

XrSystemSpaceWarpPropertiesFB system_space_warp_properties = {
XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB, // type
nullptr, // next
0, // recommendedMotionVectorImageRectWidth
0, // recommendedMotionVectorImageRectHeight
};

uint64_t motion_vector_swapchain_info = 0;
uint64_t motion_vector_depth_swapchain_info = 0;

XrCompositionLayerSpaceWarpInfoFB *space_warp_info;

std::map<godot::String, bool *> request_extensions;
bool fb_space_warp_ext = false;

struct RenderState {
bool skip_space_warp_frame = false;
Transform3D previous_transform = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } };
} render_state;
};

#endif // OPENXR_FB_SPACE_WARP_EXTENSION_WRAPPER_H
6 changes: 6 additions & 0 deletions plugin/src/main/cpp/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "extensions/openxr_fb_render_model_extension_wrapper.h"
#include "extensions/openxr_fb_scene_capture_extension_wrapper.h"
#include "extensions/openxr_fb_scene_extension_wrapper.h"
#include "extensions/openxr_fb_space_warp_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_container_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_extension_wrapper.h"
#include "extensions/openxr_fb_spatial_entity_query_extension_wrapper.h"
Expand Down Expand Up @@ -120,6 +121,9 @@ void initialize_plugin_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<OpenXRFbSceneExtensionWrapper>();
OpenXRFbSceneExtensionWrapper::get_singleton()->register_extension_wrapper();

ClassDB::register_class<OpenXRFbSpaceWarpExtensionWrapper>();
OpenXRFbSpaceWarpExtensionWrapper::get_singleton()->register_extension_wrapper();

ClassDB::register_class<OpenXRFbFaceTrackingExtensionWrapper>();
OpenXRFbFaceTrackingExtensionWrapper::get_singleton()->register_extension_wrapper();

Expand Down Expand Up @@ -166,6 +170,7 @@ void initialize_plugin_module(ModuleInitializationLevel p_level) {
Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityQueryExtensionWrapper", OpenXRFbSpatialEntityQueryExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbSpatialEntityContainerExtensionWrapper", OpenXRFbSpatialEntityContainerExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbSceneExtensionWrapper", OpenXRFbSceneExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbSpaceWarpExtensionWrapper", OpenXRFbSpaceWarpExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbHandTrackingAimExtensionWrapper", OpenXRFbHandTrackingAimExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbHandTrackingCapsulesExtensionWrapper", OpenXRFbHandTrackingCapsulesExtensionWrapper::get_singleton());
Engine::get_singleton()->register_singleton("OpenXRFbCompositionLayerSettingsExtensionWrapper", OpenXRFbCompositionLayerSettingsExtensionWrapper::get_singleton());
Expand All @@ -184,6 +189,7 @@ void initialize_plugin_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<OpenXRMetaPassthroughColorLut>();

OpenXRFbHandTrackingAimExtensionWrapper::get_singleton()->add_project_setting();
OpenXRFbSpaceWarpExtensionWrapper::get_singleton()->add_project_setting();
} break;

case MODULE_INITIALIZATION_LEVEL_EDITOR: {
Expand Down
2 changes: 1 addition & 1 deletion samples/meta-scene-sample/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ config_version=5

config/name="Meta Scene Sample"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.3", "GL Compatibility")
config/features=PackedStringArray("4.4", "GL Compatibility")
config/icon="res://icon.svg"

[layer_names]
Expand Down
Loading

0 comments on commit ae73152

Please sign in to comment.