Path: blob/master/modules/openxr/extensions/openxr_composition_layer_extension.cpp
11353 views
/**************************************************************************/1/* openxr_composition_layer_extension.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "openxr_composition_layer_extension.h"3132#ifdef ANDROID_ENABLED33#include <openxr/openxr.h>34#include <openxr/openxr_platform.h>35#endif3637#include "openxr_fb_update_swapchain_extension.h"38#include "platform/android/api/java_class_wrapper.h"39#include "servers/rendering/rendering_server_globals.h"4041////////////////////////////////////////////////////////////////////////////42// OpenXRCompositionLayerExtension4344OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::singleton = nullptr;4546OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::get_singleton() {47return singleton;48}4950OpenXRCompositionLayerExtension::OpenXRCompositionLayerExtension() {51singleton = this;52}5354OpenXRCompositionLayerExtension::~OpenXRCompositionLayerExtension() {55singleton = nullptr;56}5758HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extensions() {59HashMap<String, bool *> request_extensions;6061request_extensions[XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME] = &cylinder_ext_available;62request_extensions[XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME] = &equirect_ext_available;6364#ifdef ANDROID_ENABLED65request_extensions[XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME] = &android_surface_ext_available;66#endif6768return request_extensions;69}7071void OpenXRCompositionLayerExtension::on_instance_created(const XrInstance p_instance) {72#ifdef ANDROID_ENABLED73EXT_INIT_XR_FUNC(xrDestroySwapchain);74EXT_INIT_XR_FUNC(xrCreateSwapchainAndroidSurfaceKHR);75#endif76}7778void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) {79OpenXRAPI::get_singleton()->register_composition_layer_provider(this);80}8182void OpenXRCompositionLayerExtension::on_session_destroyed() {83OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);8485#ifdef ANDROID_ENABLED86free_queued_android_surface_swapchains();87#endif88}8990void OpenXRCompositionLayerExtension::on_pre_render() {91#ifdef ANDROID_ENABLED92free_queued_android_surface_swapchains();93#endif9495for (OpenXRViewportCompositionLayerProvider *composition_layer : composition_layers) {96composition_layer->on_pre_render();97}98}99100int OpenXRCompositionLayerExtension::get_composition_layer_count() {101return composition_layers.size();102}103104XrCompositionLayerBaseHeader *OpenXRCompositionLayerExtension::get_composition_layer(int p_index) {105ERR_FAIL_INDEX_V(p_index, composition_layers.size(), nullptr);106return composition_layers[p_index]->get_composition_layer();107}108109int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) {110ERR_FAIL_INDEX_V(p_index, composition_layers.size(), 1);111return composition_layers[p_index]->get_sort_order();112}113114void OpenXRCompositionLayerExtension::register_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {115composition_layers.push_back(p_composition_layer);116}117118void OpenXRCompositionLayerExtension::unregister_viewport_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {119composition_layers.erase(p_composition_layer);120}121122bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) {123switch (p_which) {124case XR_TYPE_COMPOSITION_LAYER_QUAD: {125// Doesn't require an extension.126return true;127} break;128case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {129return cylinder_ext_available;130} break;131case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {132return equirect_ext_available;133} break;134default: {135ERR_PRINT(vformat("Unsupported composition layer type: %s", p_which));136return false;137}138}139}140141#ifdef ANDROID_ENABLED142bool OpenXRCompositionLayerExtension::create_android_surface_swapchain(XrSwapchainCreateInfo *p_info, XrSwapchain *r_swapchain, jobject *r_surface) {143if (android_surface_ext_available) {144OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();145ERR_FAIL_NULL_V(openxr_api, false);146147XrResult result = xrCreateSwapchainAndroidSurfaceKHR(openxr_api->get_session(), p_info, r_swapchain, r_surface);148if (XR_FAILED(result)) {149print_line("OpenXR: Failed to create Android surface swapchain [", openxr_api->get_error_string(result), "]");150return false;151}152153return true;154}155156return false;157}158159void OpenXRCompositionLayerExtension::free_android_surface_swapchain(XrSwapchain p_swapchain) {160android_surface_swapchain_free_queue.push_back(p_swapchain);161}162163void OpenXRCompositionLayerExtension::free_queued_android_surface_swapchains() {164for (XrSwapchain swapchain : android_surface_swapchain_free_queue) {165xrDestroySwapchain(swapchain);166}167android_surface_swapchain_free_queue.clear();168}169#endif170171////////////////////////////////////////////////////////////////////////////172// OpenXRViewportCompositionLayerProvider173174OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer) {175composition_layer = p_composition_layer;176openxr_api = OpenXRAPI::get_singleton();177composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();178}179180OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() {181for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {182extension->on_viewport_composition_layer_destroyed(composition_layer);183}184185if (use_android_surface) {186free_swapchain();187} else {188// This will reset the viewport and free the swapchain too.189set_viewport(RID(), Size2i());190}191}192193void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) {194if (alpha_blend != p_alpha_blend) {195alpha_blend = p_alpha_blend;196if (alpha_blend) {197composition_layer->layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;198} else {199composition_layer->layerFlags &= ~XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;200}201}202}203204void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i p_size) {205ERR_FAIL_COND(use_android_surface);206207RenderingServer *rs = RenderingServer::get_singleton();208ERR_FAIL_NULL(rs);209210if (subviewport.viewport != p_viewport) {211if (subviewport.viewport.is_valid()) {212RID rt = rs->viewport_get_render_target(subviewport.viewport);213RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());214}215216subviewport.viewport = p_viewport;217218if (subviewport.viewport.is_valid()) {219subviewport.viewport_size = p_size;220} else {221free_swapchain();222subviewport.viewport_size = Size2i();223}224}225}226227void OpenXRViewportCompositionLayerProvider::set_use_android_surface(bool p_use_android_surface, Size2i p_size) {228#ifdef ANDROID_ENABLED229if (p_use_android_surface == use_android_surface) {230if (use_android_surface && swapchain_size != p_size) {231OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();232if (fb_update_swapchain_ext && fb_update_swapchain_ext->is_android_ext_enabled()) {233swapchain_size = p_size;234fb_update_swapchain_ext->update_swapchain_surface_size(android_surface.swapchain, swapchain_size);235}236}237return;238}239240use_android_surface = p_use_android_surface;241242if (use_android_surface) {243if (!composition_layer_extension->is_android_surface_swapchain_available()) {244ERR_PRINT_ONCE("OpenXR: Cannot use Android surface for composition layer because the extension isn't available");245}246247if (subviewport.viewport.is_valid()) {248set_viewport(RID(), Size2i());249}250251swapchain_size = p_size;252} else {253free_swapchain();254}255#endif256}257258#ifdef ANDROID_ENABLED259void OpenXRViewportCompositionLayerProvider::create_android_surface() {260ERR_FAIL_COND(android_surface.swapchain != XR_NULL_HANDLE || android_surface.surface.is_valid());261ERR_FAIL_COND(!openxr_api || !openxr_api->is_running());262263void *next_pointer = nullptr;264for (OpenXRExtensionWrapper *wrapper : openxr_api->get_registered_extension_wrappers()) {265void *np = wrapper->set_android_surface_swapchain_create_info_and_get_next_pointer(extension_property_values, next_pointer);266if (np != nullptr) {267next_pointer = np;268}269}270271// Check to see if content should be protected.272XrSwapchainCreateFlags create_flags = 0;273274if (protected_content) {275create_flags = XR_SWAPCHAIN_CREATE_PROTECTED_CONTENT_BIT;276}277278// The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount,279// faceCount, arraySize, and mipCount must be zero.280XrSwapchainCreateInfo info = {281XR_TYPE_SWAPCHAIN_CREATE_INFO, // type282next_pointer, // next283create_flags, // createFlags284XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags2850, // format2860, // sampleCount287(uint32_t)swapchain_size.x, // width288(uint32_t)swapchain_size.y, // height2890, // faceCount2900, // arraySize2910, // mipCount292};293294jobject surface;295composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface);296297swapchain_state.dirty = true;298299if (surface) {300android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface);301}302}303#endif304305Ref<JavaObject> OpenXRViewportCompositionLayerProvider::get_android_surface() {306#ifdef ANDROID_ENABLED307if (use_android_surface) {308if (android_surface.surface.is_null()) {309create_android_surface();310}311return android_surface.surface;312}313#endif314return Ref<JavaObject>();315}316317void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) {318extension_property_values = p_extension_property_values;319extension_property_values_changed = true;320}321322void OpenXRViewportCompositionLayerProvider::on_pre_render() {323#ifdef ANDROID_ENABLED324if (use_android_surface) {325if (android_surface.surface.is_null()) {326create_android_surface();327}328return;329}330#endif331332RenderingServer *rs = RenderingServer::get_singleton();333ERR_FAIL_NULL(rs);334335if (subviewport.viewport.is_valid() && openxr_api && openxr_api->is_running()) {336RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(subviewport.viewport);337if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) {338// Update our XR swapchain339if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {340// Render to our XR swapchain image.341RID rt = rs->viewport_get_render_target(subviewport.viewport);342RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID());343}344}345}346347if (swapchain_state.dirty) {348update_swapchain_state();349swapchain_state.dirty = false;350}351}352353XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() {354if (openxr_api == nullptr || composition_layer_extension == nullptr) {355// OpenXR not initialized or we're in the editor?356return nullptr;357}358359if (!composition_layer_extension->is_available(composition_layer->type)) {360// Selected type is not supported, ignore our layer.361return nullptr;362}363364XrSwapchainSubImage subimage = {3650, // swapchain // NOLINT(modernize-use-nullptr) - 32-bit uses non-pointer uint64366{ { 0, 0 }, { 0, 0 } }, // imageRect3670, // imageArrayIndex368};369update_swapchain_sub_image(subimage);370371if (subimage.swapchain == XR_NULL_HANDLE) {372// Don't have a swapchain to display? Ignore our layer.373return nullptr;374}375376// Update the layer struct for the swapchain.377switch (composition_layer->type) {378case XR_TYPE_COMPOSITION_LAYER_QUAD: {379XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer;380quad_layer->space = openxr_api->get_play_space();381quad_layer->subImage = subimage;382} break;383384case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {385XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer;386cylinder_layer->space = openxr_api->get_play_space();387cylinder_layer->subImage = subimage;388} break;389390case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {391XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer;392equirect_layer->space = openxr_api->get_play_space();393equirect_layer->subImage = subimage;394} break;395396default: {397return nullptr;398} break;399}400401if (extension_property_values_changed) {402extension_property_values_changed = false;403404void *next_pointer = nullptr;405for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {406void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer);407if (np) {408next_pointer = np;409}410}411composition_layer->next = next_pointer;412}413414return composition_layer;415}416417void OpenXRViewportCompositionLayerProvider::update_swapchain_state() {418OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();419if (!fb_update_swapchain_ext) {420return;421}422423#ifdef ANDROID_ENABLED424if (use_android_surface) {425if (android_surface.swapchain == XR_NULL_HANDLE) {426return;427}428429fb_update_swapchain_ext->update_swapchain_state(android_surface.swapchain, &swapchain_state);430} else431#endif432{433if (subviewport.swapchain_info.get_swapchain() == XR_NULL_HANDLE) {434return;435}436437fb_update_swapchain_ext->update_swapchain_state(subviewport.swapchain_info.get_swapchain(), &swapchain_state);438}439}440441OpenXRViewportCompositionLayerProvider::SwapchainState *OpenXRViewportCompositionLayerProvider::get_swapchain_state() {442return &swapchain_state;443}444445void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) {446#ifdef ANDROID_ENABLED447if (use_android_surface) {448r_subimage.swapchain = android_surface.swapchain;449} else450#endif451{452XrSwapchain swapchain = subviewport.swapchain_info.get_swapchain();453454if (swapchain && subviewport.swapchain_info.is_image_acquired()) {455subviewport.swapchain_info.release();456}457458r_subimage.swapchain = swapchain;459}460461r_subimage.imageRect.extent.width = swapchain_size.width;462r_subimage.imageRect.extent.height = swapchain_size.height;463}464465bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) {466ERR_FAIL_COND_V(use_android_surface, false);467468if (openxr_api == nullptr || composition_layer_extension == nullptr) {469// OpenXR not initialized or we're in the editor?470return false;471}472if (!composition_layer_extension->is_available(get_openxr_type())) {473// Selected type is not supported?474return false;475}476477// See if our current swapchain is outdated.478if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {479// If this swap chain, or the previous one, were static, then we can't reuse it.480if (swapchain_size == subviewport.viewport_size && !p_static_image && !subviewport.static_image && protected_content == subviewport.swapchain_protected_content) {481// We're all good! Just acquire it.482// We can ignore should_render here, return will be false.483bool should_render = true;484return subviewport.swapchain_info.acquire(should_render);485}486487subviewport.swapchain_info.queue_free();488}489490// Create our new swap chain491int64_t swapchain_format = openxr_api->get_color_swapchain_format();492const uint32_t sample_count = 1;493const uint32_t array_size = 1;494XrSwapchainCreateFlags create_flags = 0;495if (p_static_image) {496create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;497}498if (protected_content) {499create_flags |= XR_SWAPCHAIN_CREATE_PROTECTED_CONTENT_BIT;500}501if (!subviewport.swapchain_info.create(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, subviewport.viewport_size.width, subviewport.viewport_size.height, sample_count, array_size)) {502swapchain_size = Size2i();503return false;504}505506swapchain_state.dirty = true;507508// Acquire our image so we can start rendering into it,509// we can ignore should_render here, ret will be false.510bool should_render = true;511bool ret = subviewport.swapchain_info.acquire(should_render);512513swapchain_size = subviewport.viewport_size;514subviewport.static_image = p_static_image;515subviewport.swapchain_protected_content = protected_content;516return ret;517}518519void OpenXRViewportCompositionLayerProvider::free_swapchain() {520#ifdef ANDROID_ENABLED521if (use_android_surface) {522if (android_surface.swapchain != XR_NULL_HANDLE) {523composition_layer_extension->free_android_surface_swapchain(android_surface.swapchain);524525android_surface.swapchain = XR_NULL_HANDLE;526android_surface.surface.unref();527}528} else529#endif530{531if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {532subviewport.swapchain_info.queue_free();533}534subviewport.static_image = false;535}536537swapchain_size = Size2i();538}539540RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {541ERR_FAIL_COND_V(use_android_surface, RID());542543if (openxr_api == nullptr) {544return RID();545}546547return subviewport.swapchain_info.get_image();548}549550551