Path: blob/master/modules/openxr/extensions/openxr_composition_layer_extension.cpp
10278 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// The XR_FB_android_surface_swapchain_create extension mandates that format, sampleCount,272// faceCount, arraySize, and mipCount must be zero.273XrSwapchainCreateInfo info = {274XR_TYPE_SWAPCHAIN_CREATE_INFO, // type275next_pointer, // next2760, // createFlags277XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, // usageFlags2780, // format2790, // sampleCount280(uint32_t)swapchain_size.x, // width281(uint32_t)swapchain_size.y, // height2820, // faceCount2830, // arraySize2840, // mipCount285};286287jobject surface;288composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface);289290swapchain_state.dirty = true;291292if (surface) {293android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface);294}295}296#endif297298Ref<JavaObject> OpenXRViewportCompositionLayerProvider::get_android_surface() {299#ifdef ANDROID_ENABLED300if (use_android_surface) {301if (android_surface.surface.is_null()) {302create_android_surface();303}304return android_surface.surface;305}306#endif307return Ref<JavaObject>();308}309310void OpenXRViewportCompositionLayerProvider::set_extension_property_values(const Dictionary &p_extension_property_values) {311extension_property_values = p_extension_property_values;312extension_property_values_changed = true;313}314315void OpenXRViewportCompositionLayerProvider::on_pre_render() {316#ifdef ANDROID_ENABLED317if (use_android_surface) {318if (android_surface.surface.is_null()) {319create_android_surface();320}321return;322}323#endif324325RenderingServer *rs = RenderingServer::get_singleton();326ERR_FAIL_NULL(rs);327328if (subviewport.viewport.is_valid() && openxr_api && openxr_api->is_running()) {329RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(subviewport.viewport);330if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) {331// Update our XR swapchain332if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {333// Render to our XR swapchain image.334RID rt = rs->viewport_get_render_target(subviewport.viewport);335RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID(), RID());336}337}338}339340if (swapchain_state.dirty) {341update_swapchain_state();342swapchain_state.dirty = false;343}344}345346XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() {347if (openxr_api == nullptr || composition_layer_extension == nullptr) {348// OpenXR not initialized or we're in the editor?349return nullptr;350}351352if (!composition_layer_extension->is_available(composition_layer->type)) {353// Selected type is not supported, ignore our layer.354return nullptr;355}356357XrSwapchainSubImage subimage = {3580, // swapchain // NOLINT(modernize-use-nullptr) - 32-bit uses non-pointer uint64359{ { 0, 0 }, { 0, 0 } }, // imageRect3600, // imageArrayIndex361};362update_swapchain_sub_image(subimage);363364if (subimage.swapchain == XR_NULL_HANDLE) {365// Don't have a swapchain to display? Ignore our layer.366return nullptr;367}368369// Update the layer struct for the swapchain.370switch (composition_layer->type) {371case XR_TYPE_COMPOSITION_LAYER_QUAD: {372XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer;373quad_layer->space = openxr_api->get_play_space();374quad_layer->subImage = subimage;375} break;376377case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {378XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer;379cylinder_layer->space = openxr_api->get_play_space();380cylinder_layer->subImage = subimage;381} break;382383case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {384XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer;385equirect_layer->space = openxr_api->get_play_space();386equirect_layer->subImage = subimage;387} break;388389default: {390return nullptr;391} break;392}393394if (extension_property_values_changed) {395extension_property_values_changed = false;396397void *next_pointer = nullptr;398for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {399void *np = extension->set_viewport_composition_layer_and_get_next_pointer(composition_layer, extension_property_values, next_pointer);400if (np) {401next_pointer = np;402}403}404composition_layer->next = next_pointer;405}406407return composition_layer;408}409410void OpenXRViewportCompositionLayerProvider::update_swapchain_state() {411OpenXRFBUpdateSwapchainExtension *fb_update_swapchain_ext = OpenXRFBUpdateSwapchainExtension::get_singleton();412if (!fb_update_swapchain_ext) {413return;414}415416#ifdef ANDROID_ENABLED417if (use_android_surface) {418if (android_surface.swapchain == XR_NULL_HANDLE) {419return;420}421422fb_update_swapchain_ext->update_swapchain_state(android_surface.swapchain, &swapchain_state);423} else424#endif425{426if (subviewport.swapchain_info.get_swapchain() == XR_NULL_HANDLE) {427return;428}429430fb_update_swapchain_ext->update_swapchain_state(subviewport.swapchain_info.get_swapchain(), &swapchain_state);431}432}433434OpenXRViewportCompositionLayerProvider::SwapchainState *OpenXRViewportCompositionLayerProvider::get_swapchain_state() {435return &swapchain_state;436}437438void OpenXRViewportCompositionLayerProvider::update_swapchain_sub_image(XrSwapchainSubImage &r_subimage) {439#ifdef ANDROID_ENABLED440if (use_android_surface) {441r_subimage.swapchain = android_surface.swapchain;442} else443#endif444{445XrSwapchain swapchain = subviewport.swapchain_info.get_swapchain();446447if (swapchain && subviewport.swapchain_info.is_image_acquired()) {448subviewport.swapchain_info.release();449}450451r_subimage.swapchain = swapchain;452}453454r_subimage.imageRect.extent.width = swapchain_size.width;455r_subimage.imageRect.extent.height = swapchain_size.height;456}457458bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) {459ERR_FAIL_COND_V(use_android_surface, false);460461if (openxr_api == nullptr || composition_layer_extension == nullptr) {462// OpenXR not initialized or we're in the editor?463return false;464}465if (!composition_layer_extension->is_available(get_openxr_type())) {466// Selected type is not supported?467return false;468}469470// See if our current swapchain is outdated.471if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {472// If this swap chain, or the previous one, were static, then we can't reuse it.473if (swapchain_size == subviewport.viewport_size && !p_static_image && !subviewport.static_image) {474// We're all good! Just acquire it.475// We can ignore should_render here, return will be false.476bool should_render = true;477return subviewport.swapchain_info.acquire(should_render);478}479480subviewport.swapchain_info.queue_free();481}482483// Create our new swap chain484int64_t swapchain_format = openxr_api->get_color_swapchain_format();485const uint32_t sample_count = 1;486const uint32_t array_size = 1;487XrSwapchainCreateFlags create_flags = 0;488if (p_static_image) {489create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;490}491if (!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)) {492swapchain_size = Size2i();493return false;494}495496swapchain_state.dirty = true;497498// Acquire our image so we can start rendering into it,499// we can ignore should_render here, ret will be false.500bool should_render = true;501bool ret = subviewport.swapchain_info.acquire(should_render);502503swapchain_size = subviewport.viewport_size;504subviewport.static_image = p_static_image;505return ret;506}507508void OpenXRViewportCompositionLayerProvider::free_swapchain() {509#ifdef ANDROID_ENABLED510if (use_android_surface) {511if (android_surface.swapchain != XR_NULL_HANDLE) {512composition_layer_extension->free_android_surface_swapchain(android_surface.swapchain);513514android_surface.swapchain = XR_NULL_HANDLE;515android_surface.surface.unref();516}517} else518#endif519{520if (subviewport.swapchain_info.get_swapchain() != XR_NULL_HANDLE) {521subviewport.swapchain_info.queue_free();522}523subviewport.static_image = false;524}525526swapchain_size = Size2i();527}528529RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {530ERR_FAIL_COND_V(use_android_surface, RID());531532if (openxr_api == nullptr) {533return RID();534}535536return subviewport.swapchain_info.get_image();537}538539540