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;