diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 442dcc7d18..7f72dd496c 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -547,6 +547,21 @@ + + + + + + + + + + + + + Create a texture object based an image handle already created within an GDNative extension. + + diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index d2bb6aa59a..402be4470c 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -36,6 +36,16 @@ + + + + + + + + + + diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 8a070313f8..1654a369c4 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -3622,6 +3622,12 @@ RID RasterizerStorageGLES3::render_target_create() { return render_target_owner.make_rid(rt); } +void RasterizerStorageGLES3::render_target_update(RID p_render_target) { + // TODO need to look into if this applies here and how to implement it. + // The idea here is not to create the textures and framebuffer until we need it for the first time. + // Especially when XR is used we may end up creating things multiple times and then not using it. +} + void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int p_x, int p_y) { #ifdef OPENGL_DISABLE_RENDER_TARGETS return; @@ -3671,113 +3677,14 @@ RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) { } } -void RasterizerStorageGLES3::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { +void RasterizerStorageGLES3::render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth) { #ifdef OPENGL_DISABLE_RENDER_TARGETS return; #endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); + // TODO re-implement this once stereo support has been added - if (p_texture_id == 0) { - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); - - // and this - if (rt->external.depth != 0) { - glDeleteRenderbuffers(1, &rt->external.depth); - } - - // clean up our texture - Texture *t = texture_owner.get_or_null(rt->external.texture); - t->alloc_height = 0; - t->alloc_width = 0; - t->width = 0; - t->height = 0; - t->active = false; - texture_owner.free(rt->external.texture); - memdelete(t); - - rt->external.fbo = 0; - rt->external.color = 0; - rt->external.depth = 0; - } - } else { - Texture *t; - - if (rt->external.fbo == 0) { - // create our fbo - glGenFramebuffers(1, &rt->external.fbo); - bind_framebuffer(rt->external.fbo); - - // allocate a texture - t = memnew(Texture); - - t->type = RenderingDevice::TEXTURE_TYPE_2D; - t->flags = 0; - t->width = 0; - t->height = 0; - t->alloc_height = 0; - t->alloc_width = 0; - t->format = Image::FORMAT_RGBA8; - t->target = GL_TEXTURE_2D; - t->gl_format_cache = 0; - t->gl_internal_format_cache = 0; - t->gl_type_cache = 0; - t->data_size = 0; - t->compressed = false; - t->srgb = false; - t->total_data_size = 0; - t->ignore_mipmaps = false; - t->mipmaps = 1; - t->active = true; - t->tex_id = 0; - t->render_target = rt; - - rt->external.texture = texture_owner.make_rid(t); - - } else { - // bind our frame buffer - bind_framebuffer(rt->external.fbo); - - // find our texture - t = texture_owner.get_or_null(rt->external.texture); - } - - // set our texture - t->tex_id = p_texture_id; - rt->external.color = p_texture_id; - - // size shouldn't be different - t->width = rt->width; - t->height = rt->height; - t->alloc_height = rt->width; - t->alloc_width = rt->height; - - // Switch our texture on our frame buffer - { - // set our texture as the destination for our framebuffer - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0); - - // seeing we're rendering into this directly, better also use our depth buffer, just use our existing one :) - if (config.support_depth_texture) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); - } else { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); - } - } - - // check status and unbind - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - bind_framebuffer_system(); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - printf("framebuffer fail, status: %x\n", status); - } - - ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); - } + return; } void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index eb5614f70f..6ab968ef9a 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -1256,10 +1256,11 @@ public: void _set_current_render_target(RID p_render_target); RID render_target_create() override; + void render_target_update(RID p_render_target) override; void render_target_set_position(RID p_render_target, int p_x, int p_y) override; void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override; RID render_target_get_texture(RID p_render_target) override; - void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; + void render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth) override; void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override; bool render_target_was_used(RID p_render_target) override; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 4cae051302..4010b2ed61 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -2170,6 +2170,125 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID return id; } +RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) { + _THREAD_SAFE_METHOD_ + + // This method creates a texture object using a VkImage created by an extension. + VkImage image = (VkImage) p_image; + + Texture texture; + texture.image = image; + // if we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? + // also leave texture.allocation_info alone + // we'll set texture.view later on + texture.type = p_type; + texture.format = p_format; + texture.samples = p_samples; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.mipmaps = 0; // maybe make this settable too? + texture.usage_flags = p_flags; + texture.base_mipmap = 0; + texture.base_layer = 0; + + // Do we need to do something with texture.allowed_shared_formats? + + // Do we need to do something with texture.layout ? + + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + + // if (format_has_stencil(p_format.format)) { + // texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + // } + } else { + texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + // Create a view for us to use + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_3D, + VK_IMAGE_VIEW_TYPE_CUBE, + VK_IMAGE_VIEW_TYPE_1D_ARRAY, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + }; + + image_view_create_info.viewType = view_types[texture.type]; + image_view_create_info.format = vulkan_formats[texture.format]; + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + // hardcode for now, mayb make this settable from outside.. + image_view_create_info.components.r = component_swizzles[TEXTURE_SWIZZLE_R]; + image_view_create_info.components.g = component_swizzles[TEXTURE_SWIZZLE_G]; + image_view_create_info.components.b = component_swizzles[TEXTURE_SWIZZLE_B]; + image_view_create_info.components.a = component_swizzles[TEXTURE_SWIZZLE_A]; + + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = texture.mipmaps; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = texture.layers; + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); + + if (err) { + // vmaDestroyImage(allocator, texture.image, texture.allocation); + ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); + } + + //barrier to set layout + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = texture.layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture.image; + image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = texture.mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = texture.layers; + + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + } + + RID id = texture_owner.make_rid(texture); + + return id; +} + RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type) { _THREAD_SAFE_METHOD_ @@ -8714,7 +8833,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { WARN_PRINT("Deleted a texture while it was bound.."); } vkDestroyImageView(device, texture->view, nullptr); - if (texture->owner.is_null()) { + if (texture->owner.is_null() && texture->allocation != nullptr) { //actually owns the image and the allocation too image_memory -= texture->allocation_info.size; vmaDestroyImage(allocator, texture->image, texture->allocation); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index cf0b725cfc..fd2638092f 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1036,6 +1036,7 @@ class RenderingDeviceVulkan : public RenderingDevice { public: virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector> &p_data = Vector>()); virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); + virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index c178a68236..cc791a4740 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -339,6 +339,25 @@ Error VulkanContext::_initialize_extensions() { if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; } + + // TODO Need to switch these, they are needed for OpenXR + + if (!strcmp(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + } + + if (!strcmp(VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + } + +#ifdef WINDOWS_ENABLED + if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, instance_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; + } +#endif + + // END TODO + if (enabled_extension_count >= MAX_EXTENSIONS) { free(instance_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -784,6 +803,33 @@ Error VulkanContext::_create_physical_device() { // if multiview is supported, enable it extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; } + + // TODO Need to switch these, tehy are needed for OpenXR (or do we just enable them anyway?) + if (!strcmp(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME; + } + if (!strcmp(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_EXT_DEBUG_MARKER_EXTENSION_NAME; + } +#ifdef WINDOWS_ENABLED + if (!strcmp(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, device_extensions[i].extensionName)) { + extension_names[enabled_extension_count++] = VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME; + } +#endif + // END TODO + if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp new file mode 100644 index 0000000000..5017a143c3 --- /dev/null +++ b/modules/gdnative/xr/xr_interface_gdnative.cpp @@ -0,0 +1,438 @@ +/*************************************************************************/ +/* xr_interface_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.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 "xr_interface_gdnative.h" +#include "core/input/input.h" +#include "servers/rendering/rendering_server_globals.h" +#include "servers/xr/xr_positional_tracker.h" + +void XRInterfaceGDNative::_bind_methods() { + ADD_PROPERTY_DEFAULT("interface_is_initialized", false); + ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); +} + +XRInterfaceGDNative::XRInterfaceGDNative() { + print_verbose("Construct gdnative interface\n"); + + // we won't have our data pointer until our library gets set + data = nullptr; + + interface = nullptr; +} + +XRInterfaceGDNative::~XRInterfaceGDNative() { + print_verbose("Destruct gdnative interface\n"); + + if (interface != nullptr && is_initialized()) { + uninitialize(); + }; + + // cleanup after ourselves + cleanup(); +} + +void XRInterfaceGDNative::cleanup() { + if (interface != nullptr) { + interface->destructor(data); + data = nullptr; + interface = nullptr; + } +} + +void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_interface) { + // this should only be called once, just being paranoid.. + if (interface) { + cleanup(); + interface = NULL; + } + + // validate + ERR_FAIL_NULL(p_interface); + ERR_FAIL_COND_MSG(p_interface->version.major < 4, "This is an incompatible GDNative XR plugin."); + + // bind to our interface + interface = p_interface; + + // Now we do our constructing... + data = interface->constructor((godot_object *)this); +} + +StringName XRInterfaceGDNative::get_name() const { + ERR_FAIL_COND_V(interface == nullptr, StringName()); + + godot_string result = interface->get_name(data); + + StringName name = *(String *)&result; + + godot_string_destroy(&result); + + return name; +} + +int XRInterfaceGDNative::get_capabilities() const { + int capabilities; + + ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None + + capabilities = interface->get_capabilities(data); + + return capabilities; +} + +bool XRInterfaceGDNative::get_anchor_detection_is_enabled() const { + ERR_FAIL_COND_V(interface == nullptr, false); + + return interface->get_anchor_detection_is_enabled(data); +} + +void XRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { + ERR_FAIL_COND(interface == nullptr); + + interface->set_anchor_detection_is_enabled(data, p_enable); +} + +int XRInterfaceGDNative::get_camera_feed_id() { + ERR_FAIL_COND_V(interface == nullptr, 0); + + return (unsigned int)interface->get_camera_feed_id(data); +} + +uint32_t XRInterfaceGDNative::get_view_count() { + uint32_t view_count; + + ERR_FAIL_COND_V(interface == nullptr, 1); + + view_count = interface->get_view_count(data); + + return view_count; +} + +bool XRInterfaceGDNative::is_initialized() const { + ERR_FAIL_COND_V(interface == nullptr, false); + + return interface->is_initialized(data); +} + +bool XRInterfaceGDNative::initialize() { + ERR_FAIL_COND_V(interface == nullptr, false); + + bool initialized = interface->initialize(data); + + if (initialized) { + // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface + + XRServer *xr_server = XRServer::get_singleton(); + if ((xr_server != nullptr) && (xr_server->get_primary_interface() == nullptr)) { + xr_server->set_primary_interface(this); + }; + }; + + return initialized; +} + +void XRInterfaceGDNative::uninitialize() { + ERR_FAIL_COND(interface == nullptr); + + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + // Whatever happens, make sure this is no longer our primary interface + xr_server->clear_primary_interface_if(this); + } + + interface->uninitialize(data); +} + +Size2 XRInterfaceGDNative::get_render_targetsize() { + ERR_FAIL_COND_V(interface == nullptr, Size2()); + + godot_vector2 result = interface->get_render_targetsize(data); + Vector2 *vec = (Vector2 *)&result; + + return *vec; +} + +Transform3D XRInterfaceGDNative::get_camera_transform() { + Transform3D *ret; + + ERR_FAIL_COND_V(interface == nullptr, Transform3D()); + + godot_transform3d t = interface->get_camera_transform(data); + + ret = (Transform3D *)&t; + + return *ret; +} + +Transform3D XRInterfaceGDNative::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { + Transform3D *ret; + + ERR_FAIL_COND_V(interface == nullptr, Transform3D()); + + godot_transform3d t = interface->get_transform_for_view(data, (int)p_view, (godot_transform3d *)&p_cam_transform); + + ret = (Transform3D *)&t; + + return *ret; +} + +CameraMatrix XRInterfaceGDNative::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix cm; + + ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); + + interface->fill_projection_for_view(data, (godot_real_t *)cm.matrix, (godot_int)p_view, p_aspect, p_z_near, p_z_far); + + return cm; +} + +Vector XRInterfaceGDNative::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { + // possibly move this as a member variable and add a callback to populate? + Vector blit_to_screen; + + ERR_FAIL_COND_V(interface == nullptr, blit_to_screen); + + // must implement + interface->commit_views(data, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); + + return blit_to_screen; +} + +void XRInterfaceGDNative::process() { + ERR_FAIL_COND(interface == nullptr); + + interface->process(data); +} + +void XRInterfaceGDNative::notification(int p_what) { + ERR_FAIL_COND(interface == nullptr); + + interface->notification(data, p_what); +} + +///////////////////////////////////////////////////////////////////////////////////// +// some helper callbacks + +extern "C" { + +void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface) { + // Must be on a version 4 plugin + ERR_FAIL_COND_MSG(p_interface->version.major < 4, "GDNative XR interfaces build for Godot 3.x are not supported."); + + Ref new_interface; + new_interface.instantiate(); + new_interface->set_interface((const godot_xr_interface_gdnative *)p_interface); + XRServer::get_singleton()->add_interface(new_interface); +} + +godot_real_t GDAPI godot_xr_get_worldscale() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 1.0); + + return xr_server->get_world_scale(); +} + +godot_transform3d GDAPI godot_xr_get_reference_frame() { + godot_transform3d reference_frame; + Transform3D *reference_frame_ptr = (Transform3D *)&reference_frame; + + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + *reference_frame_ptr = xr_server->get_reference_frame(); + } else { + memnew_placement(&reference_frame, Transform3D); + } + + return reference_frame; +} + +void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { + // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD + XRInterface::Eyes eye = (XRInterface::Eyes)p_eye; +#if 0 + RID *render_target = (RID *)p_render_target; +#endif + Rect2 screen_rect = *(Rect2 *)p_rect; + + if (eye == XRInterface::EYE_LEFT) { + screen_rect.size.x /= 2.0; + } else if (p_eye == XRInterface::EYE_RIGHT) { + screen_rect.size.x /= 2.0; + screen_rect.position.x += screen_rect.size.x; + } +#ifndef _MSC_VER +#warning this needs to be redone +#endif +#if 0 + RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); +#endif +} + +godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target) { + // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID + // This is a handy function to expose that. +#if 0 + RID *render_target = (RID *)p_render_target; + + RID eye_texture = RSG::storage->render_target_get_texture(*render_target); +#endif + +#ifndef _MSC_VER +#warning need to obtain this ID again +#endif + uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture); + + return texid; +} + +godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0); + + Input *input = Input::get_singleton(); + ERR_FAIL_NULL_V(input, 0); + + Ref new_tracker; + new_tracker.instantiate(); + new_tracker->set_tracker_name(p_device_name); + new_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); + if (p_hand == 1) { + new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT); + } else if (p_hand == 2) { + new_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT); + } + + // also register as joystick... + int joyid = input->get_unused_joy_id(); + if (joyid != -1) { + new_tracker->set_joy_id(joyid); + input->joy_connection_changed(joyid, true, p_device_name, ""); + } + + if (p_tracks_orientation) { + Basis orientation; + new_tracker->set_orientation(orientation); + } + if (p_tracks_position) { + Vector3 position; + new_tracker->set_position(position); + } + + // add our tracker to our server and remember its pointer + xr_server->add_tracker(new_tracker); + + // note, this ID is only unique within controllers! + return new_tracker->get_tracker_id(); +} + +void GDAPI godot_xr_remove_controller(godot_int p_controller_id) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Input *input = Input::get_singleton(); + ERR_FAIL_NULL(input); + + Ref remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (remove_tracker.is_valid()) { + // unset our joystick if applicable + int joyid = remove_tracker->get_joy_id(); + if (joyid != -1) { + input->joy_connection_changed(joyid, false, "", ""); + remove_tracker->set_joy_id(-1); + } + + // remove our tracker from our server + xr_server->remove_tracker(remove_tracker); + remove_tracker.unref(); + } +} + +void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Ref tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker.is_valid()) { + Transform3D *transform = (Transform3D *)p_transform; + if (p_tracks_orientation) { + tracker->set_orientation(transform->basis); + } + if (p_tracks_position) { + tracker->set_rw_position(transform->origin); + } + } +} + +void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Input *input = Input::get_singleton(); + ERR_FAIL_NULL(input); + + Ref tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker.is_valid()) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + input->joy_button(joyid, (JoyButton)p_button, p_is_pressed); + } + } +} + +void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + Input *input = Input::get_singleton(); + ERR_FAIL_NULL(input); + + Ref tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker.is_valid()) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + Input::JoyAxisValue jx; + jx.min = p_can_be_negative ? -1 : 0; + jx.value = p_value; + input->joy_axis(joyid, (JoyAxis)p_axis, jx); + } + } +} + +godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0.0); + + Ref tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker.is_valid()) { + return tracker->get_rumble(); + } + + return 0.0; +} +} diff --git a/servers/rendering/rasterizer_dummy.h b/servers/rendering/rasterizer_dummy.h index 44e07a1853..6c675be040 100644 --- a/servers/rendering/rasterizer_dummy.h +++ b/servers/rendering/rasterizer_dummy.h @@ -659,10 +659,11 @@ public: /* RENDER TARGET */ RID render_target_create() override { return RID(); } + void render_target_update(RID p_render_target) override {} void render_target_set_position(RID p_render_target, int p_x, int p_y) override {} void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override {} RID render_target_get_texture(RID p_render_target) override { return RID(); } - void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {} + void render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth) override {} void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {} bool render_target_was_used(RID p_render_target) override { return false; } void render_target_set_as_unused(RID p_render_target) override {} diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 5c7fee7ec9..d4d42e9f0e 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -7467,6 +7467,20 @@ AABB RendererStorageRD::lightmap_get_aabb(RID p_lightmap) const { /* RENDER TARGET API */ +void RendererStorageRD::_mark_render_target_dirty(RenderTarget *rt) { + if (rt->texture.is_null()) { + //create a placeholder until updated + rt->texture = texture_allocate(); + texture_2d_placeholder_initialize(rt->texture); + Texture *tex = texture_owner.get_or_null(rt->texture); + tex->is_render_target = true; + } + + _clear_render_target(rt); + + rt->is_dirty = true; +} + void RendererStorageRD::_clear_render_target(RenderTarget *rt) { //free in reverse dependency order if (rt->framebuffer.is_valid()) { @@ -7509,81 +7523,137 @@ void RendererStorageRD::_update_render_target(RenderTarget *rt) { if (rt->size.width == 0 || rt->size.height == 0) { return; } - //until we implement support for HDR monitors (and render target is attached to screen), this is enough. - rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; - rt->image_format = rt->flags[RENDER_TARGET_TRANSPARENT] ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; - RD::TextureFormat rd_format; - RD::TextureView rd_view; - { //attempt register - rd_format.format = rt->color_format; - rd_format.width = rt->size.width; - rd_format.height = rt->size.height; - rd_format.depth = 1; - rd_format.array_layers = rt->view_count; // for stereo we create two (or more) layers, need to see if we can make fallback work like this too if we don't have multiview - rd_format.mipmaps = 1; - if (rd_format.array_layers > 1) { // why are we not using rt->texture_type ?? - rd_format.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; - } else { - rd_format.texture_type = RD::TEXTURE_TYPE_2D; - } - rd_format.samples = RD::TEXTURE_SAMPLES_1; - rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - rd_format.shareable_formats.push_back(rt->color_format); - rd_format.shareable_formats.push_back(rt->color_format_srgb); - } + if (rt->external_color.is_null()) { + //until we implement support for HDR monitors (and render target is attached to screen), this is enough. + rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + rt->image_format = rt->flags[RENDER_TARGET_TRANSPARENT] ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; - rt->color = RD::get_singleton()->texture_create(rd_format, rd_view); - ERR_FAIL_COND(rt->color.is_null()); - - Vector fb_textures; - fb_textures.push_back(rt->color); - rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count); - if (rt->framebuffer.is_null()) { - _clear_render_target(rt); - ERR_FAIL_COND(rt->framebuffer.is_null()); - } - - { //update texture - - Texture *tex = texture_owner.get_or_null(rt->texture); - - //free existing textures - if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { - RD::get_singleton()->free(tex->rd_texture); - } - if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { - RD::get_singleton()->free(tex->rd_texture_srgb); + RD::TextureFormat rd_format; + RD::TextureView rd_view; + { //attempt register + rd_format.format = rt->color_format; + rd_format.width = rt->size.width; + rd_format.height = rt->size.height; + rd_format.depth = 1; + rd_format.array_layers = rt->view_count; // for stereo we create two (or more) layers, need to see if we can make fallback work like this too if we don't have multiview + rd_format.mipmaps = 1; + if (rd_format.array_layers > 1) { // why are we not using rt->texture_type ?? + rd_format.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; + } else { + rd_format.texture_type = RD::TEXTURE_TYPE_2D; + } + rd_format.samples = RD::TEXTURE_SAMPLES_1; + rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + rd_format.shareable_formats.push_back(rt->color_format); + rd_format.shareable_formats.push_back(rt->color_format_srgb); } - tex->rd_texture = RID(); - tex->rd_texture_srgb = RID(); + rt->color = RD::get_singleton()->texture_create(rd_format, rd_view); + ERR_FAIL_COND(rt->color.is_null()); - //create shared textures to the color buffer, - //so transparent can be supported - RD::TextureView view; - view.format_override = rt->color_format; - if (!rt->flags[RENDER_TARGET_TRANSPARENT]) { - view.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + Vector fb_textures; + fb_textures.push_back(rt->color); + rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count); + if (rt->framebuffer.is_null()) { + _clear_render_target(rt); + ERR_FAIL_COND(rt->framebuffer.is_null()); } - tex->rd_texture = RD::get_singleton()->texture_create_shared(view, rt->color); - if (rt->color_format_srgb != RD::DATA_FORMAT_MAX) { - view.format_override = rt->color_format_srgb; - tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(view, rt->color); - } - tex->rd_view = view; - tex->width = rt->size.width; - tex->height = rt->size.height; - tex->width_2d = rt->size.width; - tex->height_2d = rt->size.height; - tex->rd_format = rt->color_format; - tex->rd_format_srgb = rt->color_format_srgb; - tex->format = rt->image_format; - Vector proxies = tex->proxies; //make a copy, since update may change it - for (int i = 0; i < proxies.size(); i++) { - texture_proxy_update(proxies[i], rt->texture); + { //update texture + + Texture *tex = texture_owner.get_or_null(rt->texture); + + //free existing textures + if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { + RD::get_singleton()->free(tex->rd_texture); + } + if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { + RD::get_singleton()->free(tex->rd_texture_srgb); + } + + tex->rd_texture = RID(); + tex->rd_texture_srgb = RID(); + + //create shared textures to the color buffer, + //so transparent can be supported + RD::TextureView view; + view.format_override = rt->color_format; + if (!rt->flags[RENDER_TARGET_TRANSPARENT]) { + view.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } + tex->rd_texture = RD::get_singleton()->texture_create_shared(view, rt->color); + if (rt->color_format_srgb != RD::DATA_FORMAT_MAX) { + view.format_override = rt->color_format_srgb; + tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(view, rt->color); + } + tex->rd_view = view; + tex->width = rt->size.width; + tex->height = rt->size.height; + tex->width_2d = rt->size.width; + tex->height_2d = rt->size.height; + tex->rd_format = rt->color_format; + tex->rd_format_srgb = rt->color_format_srgb; + tex->format = rt->image_format; + + Vector proxies = tex->proxies; //make a copy, since update may change it + for (int i = 0; i < proxies.size(); i++) { + texture_proxy_update(proxies[i], rt->texture); + } + } + } else { + // use our external texture + + Vector fb_textures; + fb_textures.push_back(rt->external_color); + rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count); + if (rt->framebuffer.is_null()) { + _clear_render_target(rt); + ERR_FAIL_COND(rt->framebuffer.is_null()); + } + + { //update texture + // TODO change this to get color info from our external texture instead... + + Texture *tex = texture_owner.get_or_null(rt->texture); + + //free existing textures + if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { + RD::get_singleton()->free(tex->rd_texture); + } + if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { + RD::get_singleton()->free(tex->rd_texture_srgb); + } + + tex->rd_texture = RID(); + tex->rd_texture_srgb = RID(); + + //create shared textures to the color buffer, + //so transparent can be supported + RD::TextureView view; + view.format_override = rt->color_format; + if (!rt->flags[RENDER_TARGET_TRANSPARENT]) { + view.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; + } + tex->rd_texture = RD::get_singleton()->texture_create_shared(view, rt->external_color); + if (rt->color_format_srgb != RD::DATA_FORMAT_MAX) { + view.format_override = rt->color_format_srgb; + tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(view, rt->external_color); + } + tex->rd_view = view; + tex->width = rt->size.width; + tex->height = rt->size.height; + tex->width_2d = rt->size.width; + tex->height_2d = rt->size.height; + tex->rd_format = rt->color_format; + tex->rd_format_srgb = rt->color_format_srgb; + tex->format = rt->image_format; + + Vector proxies = tex->proxies; //make a copy, since update may change it + for (int i = 0; i < proxies.size(); i++) { + texture_proxy_update(proxies[i], rt->texture); + } } } } @@ -7645,10 +7715,20 @@ RID RendererStorageRD::render_target_create() { for (int i = 0; i < RENDER_TARGET_FLAG_MAX; i++) { render_target.flags[i] = false; } - _update_render_target(&render_target); + _mark_render_target_dirty(&render_target); return render_target_owner.make_rid(render_target); } +void RendererStorageRD::render_target_update(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (rt->is_dirty) { + _update_render_target(rt); + rt->is_dirty = false; + } +} + void RendererStorageRD::render_target_set_position(RID p_render_target, int p_x, int p_y) { //unused for this render target } @@ -7660,7 +7740,7 @@ void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, rt->size.x = p_width; rt->size.y = p_height; rt->view_count = p_view_count; - _update_render_target(rt); + _mark_render_target_dirty(rt); } } @@ -7671,14 +7751,22 @@ RID RendererStorageRD::render_target_get_texture(RID p_render_target) { return rt->texture; } -void RendererStorageRD::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { +void RendererStorageRD::render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (rt->external_color != p_color || rt->external_depth != p_depth) { + rt->external_color = p_color; + rt->external_depth = p_depth; + _mark_render_target_dirty(rt); + } } void RendererStorageRD::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->flags[p_flag] = p_value; - _update_render_target(rt); + _mark_render_target_dirty(rt); } bool RendererStorageRD::render_target_was_used(RID p_render_target) { diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index f13bd4a7f4..2682c51419 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -1152,6 +1152,7 @@ private: /* RENDER TARGET */ struct RenderTarget { + bool is_dirty = true; Size2i size; uint32_t view_count; RID framebuffer; @@ -1170,6 +1171,9 @@ private: RID backbuffer_fb; RID backbuffer_mipmap0; + RID external_color; // used for XR + RID external_depth; + struct BackbufferMipmap { RID mipmap; RID mipmap_copy; @@ -1200,6 +1204,7 @@ private: mutable RID_Owner render_target_owner; + void _mark_render_target_dirty(RenderTarget *rt); void _clear_render_target(RenderTarget *rt); void _update_render_target(RenderTarget *rt); void _create_render_target_backbuffer(RenderTarget *rt); @@ -2326,10 +2331,11 @@ public: /* RENDER TARGET API */ RID render_target_create(); + void render_target_update(RID p_render_target); void render_target_set_position(RID p_render_target, int p_x, int p_y); void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count); RID render_target_get_texture(RID p_render_target); - void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); + void render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth); void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); bool render_target_was_used(RID p_render_target); void render_target_set_as_unused(RID p_render_target); diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h index 2985e05e8f..f72e19e2a5 100644 --- a/servers/rendering/renderer_storage.h +++ b/servers/rendering/renderer_storage.h @@ -588,10 +588,11 @@ public: }; virtual RID render_target_create() = 0; + virtual void render_target_update(RID p_render_target) = 0; virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0; virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) = 0; virtual RID render_target_get_texture(RID p_render_target) = 0; - virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0; + virtual void render_target_set_external_textures(RID p_render_target, RID p_color, RID p_depth) = 0; virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0; virtual bool render_target_was_used(RID p_render_target) = 0; virtual void render_target_set_as_unused(RID p_render_target) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index c3d57a13ad..920b30dd3e 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -573,9 +573,11 @@ void RendererViewport::draw_viewports() { uint32_t view_count = xr_interface->get_view_count(); RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count); - // check for an external texture destination (disabled for now, not yet supported) - // RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono)); - RSG::storage->render_target_set_external_texture(vp->render_target, 0); + // check for an external texture destination + RSG::storage->render_target_set_external_textures(vp->render_target, xr_interface->get_external_color_texture(), xr_interface->get_external_depth_texture()); + + // (Re)create our RT if needed + RSG::storage->render_target_update(vp->render_target); // render... RSG::scene->set_debug_draw_mode(vp->debug_draw); @@ -583,8 +585,6 @@ void RendererViewport::draw_viewports() { // and draw viewport _draw_viewport(vp); - // measure - // commit our eyes Vector blits = xr_interface->commit_views(vp->render_target, vp->viewport_to_screen_rect); if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && blits.size() > 0) { @@ -600,7 +600,14 @@ void RendererViewport::draw_viewports() { // and for our frame timing, mark when we've finished committing our eyes XRServer::get_singleton()->_mark_commit(); } else { - RSG::storage->render_target_set_external_texture(vp->render_target, 0); + // clear our external texture if set + RSG::storage->render_target_set_external_textures(vp->render_target, RID(), RID()); + + // @TODO check if our viewport is full screen and is set to output to our display, + // if so we should create an RT that uses our current swap image (maybe set that as our external texture?) + + // (Re)create our RT if needed + RSG::storage->render_target_update(vp->render_target); RSG::scene->set_debug_draw_mode(vp->debug_draw); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index dcbc5f5c8e..e5667456a3 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -361,6 +361,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_create", "format", "view", "data"), &RenderingDevice::_texture_create, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("texture_create_shared", "view", "with_texture"), &RenderingDevice::_texture_create_shared); ClassDB::bind_method(D_METHOD("texture_create_shared_from_slice", "view", "with_texture", "layer", "mipmap", "slice_type"), &RenderingDevice::_texture_create_shared_from_slice, DEFVAL(TEXTURE_SLICE_2D)); + ClassDB::bind_method(D_METHOD("texture_create_from_extension", "type", "format", "samples", "flags", "image", "width", "height", "depth", "layers"), &RenderingDevice::texture_create_from_extension); ClassDB::bind_method(D_METHOD("texture_update", "texture", "layer", "data", "post_barrier"), &RenderingDevice::texture_update, DEFVAL(BARRIER_MASK_ALL)); ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "layer"), &RenderingDevice::texture_get_data); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 5eb8f1cead..f32db932c9 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -496,6 +496,7 @@ public: virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector> &p_data = Vector>()) = 0; virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture) = 0; + virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) = 0; enum TextureSliceType { TEXTURE_SLICE_2D, diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index ca11df439c..332bc550d2 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -137,7 +137,17 @@ PackedVector3Array XRInterface::get_play_area() const { // Note implementation is responsible for applying our reference frame and world scale to the raw data. // `play_area_changed` should be emitted if play area data is available and either the reference frame or world scale changes. return PackedVector3Array(); -}; +} + +// optional render to external color texture which enhances performance on those platforms that require us to submit our end result into special textures. +RID XRInterface::get_external_color_texture() { + return RID(); +} + +// optional render to external depth texture which enhances performance on those platforms that require us to submit our end result into special textures. +RID XRInterface::get_external_depth_texture() { + return RID(); +} /** these will only be implemented on AR interfaces, so we want dummies for VR **/ bool XRInterface::get_anchor_detection_is_enabled() const { diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h index b489481f75..60e3c128f4 100644 --- a/servers/xr/xr_interface.h +++ b/servers/xr/xr_interface.h @@ -125,6 +125,8 @@ public: // note, external color/depth/vrs texture support will be added here soon. + virtual RID get_external_color_texture(); /* if applicable return external color texture to render to */ + virtual RID get_external_depth_texture(); /* if applicable return external depth texture to render to */ virtual Vector commit_views(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* commit rendered views to the XR interface */ virtual void process() = 0; diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index 80576ac607..e242e9eda3 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -52,6 +52,8 @@ void XRInterfaceExtension::_bind_methods() { GDVIRTUAL_BIND(_get_transform_for_view, "view", "cam_transform"); GDVIRTUAL_BIND(_get_projection_for_view, "view", "aspect", "z_near", "z_far"); + GDVIRTUAL_BIND(_get_external_color_texture); + GDVIRTUAL_BIND(_get_external_depth_texture); GDVIRTUAL_BIND(_commit_views, "render_target", "screen_rect"); GDVIRTUAL_BIND(_process); @@ -333,3 +335,25 @@ RID XRInterfaceExtension::get_render_target_depth(RID p_render_target) { return rd_scene->render_buffers_get_depth_texture(????????????); } */ + +// optional render to external color texture which enhances performance on those platforms that require us to submit our end result into special textures. +RID XRInterfaceExtension::get_external_color_texture() { + RID texture; + + if (GDVIRTUAL_CALL(_get_external_color_texture, texture)) { + return texture; + } + + return RID(); +}; + +// optional render to external depth texture which enhances performance on those platforms that require us to submit our end result into special textures. +RID XRInterfaceExtension::get_external_depth_texture() { + RID texture; + + if (GDVIRTUAL_CALL(_get_external_depth_texture, texture)) { + return texture; + } + + return RID(); +}; diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h index 763526de96..2a397e9cdf 100644 --- a/servers/xr/xr_interface_extension.h +++ b/servers/xr/xr_interface_extension.h @@ -108,8 +108,13 @@ public: GDVIRTUAL2R(Transform3D, _get_transform_for_view, uint32_t, const Transform3D &); GDVIRTUAL4R(PackedFloat64Array, _get_projection_for_view, uint32_t, double, double, double); + virtual RID get_external_color_texture(); /* if applicable return external color texture to render to */ + virtual RID get_external_depth_texture(); /* if applicable return external depth texture to render to */ void add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer = false, uint32_t p_layer = 0, bool p_apply_lens_distortion = false, Vector2 p_eye_center = Vector2(), double p_k1 = 0.0, double p_k2 = 0.0, double p_upscale = 1.0, double p_aspect_ratio = 1.0); virtual Vector commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; + + GDVIRTUAL0R(RID, _get_external_color_texture); + GDVIRTUAL0R(RID, _get_external_depth_texture); GDVIRTUAL2(_commit_views, RID, const Rect2 &); virtual void process() override;