Merge pull request #56394 from BastiaanOlij/OpenXR_Core4

This commit is contained in:
Rémi Verschelde 2022-02-23 13:36:07 +01:00 committed by GitHub
commit 1f3916e0b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
112 changed files with 30315 additions and 169 deletions

View File

@ -361,12 +361,16 @@ Comment: Multi-channel signed distance field generator
Copyright: 2016, Viktor Chlumsky Copyright: 2016, Viktor Chlumsky
License: MIT License: MIT
Files: ./thirdparty/oidn/ Files: ./thirdparty/oidn/
Comment: Intel Open Image Denoise Comment: Intel Open Image Denoise
Copyright: 2009-2019, Intel Corporation Copyright: 2009-2019, Intel Corporation
License: Apache-2.0 License: Apache-2.0
Files: ./thirdparty/openxr/
Comment: OpenXR Loader
Copyright: 2020-2022, The Khronos Group Inc.
License: Apache-2.0
Files: ./thirdparty/pcre2/ Files: ./thirdparty/pcre2/
Comment: PCRE2 Comment: PCRE2
Copyright: 1997-2021, University of Cambridge Copyright: 1997-2021, University of Cambridge

View File

@ -173,6 +173,7 @@ opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", Tru
opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False)) opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
opts.Add(BoolVariable("vulkan", "Enable the vulkan video driver", True)) opts.Add(BoolVariable("vulkan", "Enable the vulkan video driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 video driver", True)) opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 video driver", True))
opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "") opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "")
opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True)) opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True)) opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))

View File

@ -1915,8 +1915,23 @@
</member> </member>
<member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64"> <member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64">
</member> </member>
<member name="rendering/xr/enabled" type="bool" setter="" getter="" default="false"> <member name="xr/openxr/default_action_map" type="String" setter="" getter="" default="&quot;res://default_action_map.tres&quot;">
If [code]true[/code], XR support is enabled in Godot, this ensures required shaders are compiled. Action map configuration to load by default.
</member>
<member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false">
If [code]true[/code] Godot will setup and initialise OpenXR on startup.
</member>
<member name="xr/openxr/form_factor" type="int" setter="" getter="" default="&quot;0&quot;">
Specify whether OpenXR should be configured for an HMD or a hand held device.
</member>
<member name="xr/openxr/reference_space" type="int" setter="" getter="" default="&quot;1&quot;">
Specify the default reference space.
</member>
<member name="xr/openxr/view_configuration" type="int" setter="" getter="" default="&quot;1&quot;">
Specify the view configuration with which to configure OpenXR settting up either Mono or Stereo rendering.
</member>
<member name="xr/shaders/enabled" type="bool" setter="" getter="" default="false">
If [code]true[/code], Godot will compile shaders required for XR.
</member> </member>
</members> </members>
</class> </class>

View File

@ -41,12 +41,6 @@
</description> </description>
</method> </method>
</methods> </methods>
<members>
<member name="rumble" type="float" setter="set_rumble" getter="get_rumble" default="0.0">
The degree to which the controller vibrates. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code]. If changed, updates [member XRPositionalTracker.rumble] accordingly.
This is a useful property to animate if you want the controller to vibrate for a limited duration.
</member>
</members>
<signals> <signals>
<signal name="button_pressed"> <signal name="button_pressed">
<argument index="0" name="name" type="String" /> <argument index="0" name="name" type="String" />

View File

@ -72,9 +72,6 @@
- [code]left_hand[/code] identifies the controller held in the players left hand - [code]left_hand[/code] identifies the controller held in the players left hand
- [code]right_hand[/code] identifies the controller held in the players right hand - [code]right_hand[/code] identifies the controller held in the players right hand
</member> </member>
<member name="rumble" type="float" setter="set_rumble" getter="get_rumble" default="0.0">
The degree to which the tracker rumbles. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code].
</member>
<member name="type" type="int" setter="set_tracker_type" getter="get_tracker_type" enum="XRServer.TrackerType" default="128"> <member name="type" type="int" setter="set_tracker_type" getter="get_tracker_type" enum="XRServer.TrackerType" default="128">
The type of tracker. The type of tracker.
</member> </member>

View File

@ -2118,6 +2118,124 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID
return id; 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, module or other external source (OpenXR uses this).
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;
texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM);
texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB);
// 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, maybe 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, uint32_t p_mipmaps, TextureSliceType p_slice_type) { RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_

View File

@ -1037,6 +1037,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
public: public:
virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>());
virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); 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, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D);
virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL);

View File

@ -46,6 +46,8 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define APP_SHORT_NAME "GodotEngine" #define APP_SHORT_NAME "GodotEngine"
VulkanHooks *VulkanContext::vulkan_hooks = nullptr;
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessageTypeFlagsEXT messageType,
@ -695,19 +697,27 @@ Error VulkanContext::_create_instance() {
inst_info.pNext = &dbg_report_callback_create_info; inst_info.pNext = &dbg_report_callback_create_info;
} }
VkResult err = vkCreateInstance(&inst_info, nullptr, &inst); VkResult err;
ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE,
"Cannot find a compatible Vulkan installable client driver (ICD).\n\n" if (vulkan_hooks) {
"vkCreateInstance Failure"); if (!vulkan_hooks->create_vulkan_instance(&inst_info, &inst)) {
ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, return ERR_CANT_CREATE;
"Cannot find a specified extension library.\n" }
"Make sure your layers path is set appropriately.\n" } else {
"vkCreateInstance Failure"); err = vkCreateInstance(&inst_info, nullptr, &inst);
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE,
"vkCreateInstance failed.\n\n" "Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n" "vkCreateInstance Failure");
"Please look at the Getting Started guide for additional information.\n" ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE,
"vkCreateInstance Failure"); "Cannot find a specified extension library.\n"
"Make sure your layers path is set appropriately.\n"
"vkCreateInstance Failure");
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE,
"vkCreateInstance failed.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
"Please look at the Getting Started guide for additional information.\n"
"vkCreateInstance Failure");
}
inst_initialized = true; inst_initialized = true;
@ -820,107 +830,122 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
{ 0, nullptr }, { 0, nullptr },
}; };
// TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances.
// The device should really be a preference, but for now choosing a discrete GPU over the
// integrated one is better than the default.
int32_t device_index = -1; int32_t device_index = -1;
int type_selected = -1; if (vulkan_hooks) {
print_verbose("Vulkan devices:"); if (!vulkan_hooks->get_physical_device(&gpu)) {
for (uint32_t i = 0; i < gpu_count; ++i) { return ERR_CANT_CREATE;
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
bool present_supported = false;
uint32_t device_queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr);
VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props);
for (uint32_t j = 0; j < device_queue_family_count; j++) {
VkBool32 supports;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports);
if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) {
present_supported = true;
} else {
continue;
}
} }
String name = props.deviceName;
String vendor = "Unknown"; // not really needed but nice to print the correct entry
String dev_type; for (uint32_t i = 0; i < gpu_count; ++i) {
switch (props.deviceType) { if (physical_devices[i] == gpu) {
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { device_index = i;
dev_type = "Discrete";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
dev_type = "Integrated";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
dev_type = "Virtual";
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
dev_type = "CPU";
} break;
default: {
dev_type = "Other";
} break;
}
uint32_t vendor_idx = 0;
while (vendor_names[vendor_idx].name != nullptr) {
if (props.vendorID == vendor_names[vendor_idx].id) {
vendor = vendor_names[vendor_idx].name;
break; break;
} }
vendor_idx++;
} }
free(device_queue_props); } else {
print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances.
// The device should really be a preference, but for now choosing a discrete GPU over the
// integrated one is better than the default.
if (present_supported) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. int type_selected = -1;
print_verbose("Vulkan devices:");
for (uint32_t i = 0; i < gpu_count; ++i) {
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
bool present_supported = false;
uint32_t device_queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr);
VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props);
for (uint32_t j = 0; j < device_queue_family_count; j++) {
VkBool32 supports;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports);
if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) {
present_supported = true;
} else {
continue;
}
}
String name = props.deviceName;
String vendor = "Unknown";
String dev_type;
switch (props.deviceType) { switch (props.deviceType) {
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
if (type_selected < 4) { dev_type = "Discrete";
type_selected = 4;
device_index = i;
}
} break; } break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
if (type_selected < 3) { dev_type = "Integrated";
type_selected = 3;
device_index = i;
}
} break; } break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
if (type_selected < 2) { dev_type = "Virtual";
type_selected = 2;
device_index = i;
}
} break; } break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
if (type_selected < 1) { dev_type = "CPU";
type_selected = 1;
device_index = i;
}
} break; } break;
default: { default: {
if (type_selected < 0) { dev_type = "Other";
type_selected = 0;
device_index = i;
}
} break; } break;
} }
uint32_t vendor_idx = 0;
while (vendor_names[vendor_idx].name != nullptr) {
if (props.vendorID == vendor_names[vendor_idx].id) {
vendor = vendor_names[vendor_idx].name;
break;
}
vendor_idx++;
}
free(device_queue_props);
print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type);
if (present_supported) { // Select first supported device of preffered type: Discrete > Integrated > Virtual > CPU > Other.
switch (props.deviceType) {
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
if (type_selected < 4) {
type_selected = 4;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
if (type_selected < 3) {
type_selected = 3;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
if (type_selected < 2) {
type_selected = 2;
device_index = i;
}
} break;
case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
if (type_selected < 1) {
type_selected = 1;
device_index = i;
}
} break;
default: {
if (type_selected < 0) {
type_selected = 0;
device_index = i;
}
} break;
}
}
} }
int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU.
if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) {
device_index = user_device_index;
}
ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues.");
gpu = physical_devices[device_index];
} }
int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU.
if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) {
device_index = user_device_index;
}
ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues.");
gpu = physical_devices[device_index];
free(physical_devices); free(physical_devices);
/* Look for device extensions */ /* Look for device extensions */
@ -1148,8 +1173,14 @@ Error VulkanContext::_create_device() {
sdevice.queueCreateInfoCount = 2; sdevice.queueCreateInfoCount = 2;
} }
err = vkCreateDevice(gpu, &sdevice, nullptr, &device); if (vulkan_hooks) {
ERR_FAIL_COND_V(err, ERR_CANT_CREATE); if (!vulkan_hooks->create_vulkan_device(&sdevice, &device)) {
return ERR_CANT_CREATE;
}
} else {
err = vkCreateDevice(gpu, &sdevice, nullptr, &device);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
}
return OK; return OK;
} }

View File

@ -45,6 +45,8 @@
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#endif #endif
#include "vulkan_hooks.h"
class VulkanContext { class VulkanContext {
public: public:
struct SubgroupCapabilities { struct SubgroupCapabilities {
@ -86,6 +88,7 @@ private:
FRAME_LAG = 2 FRAME_LAG = 2
}; };
static VulkanHooks *vulkan_hooks;
VkInstance inst = VK_NULL_HANDLE; VkInstance inst = VK_NULL_HANDLE;
VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE;
VkPhysicalDeviceProperties gpu_props; VkPhysicalDeviceProperties gpu_props;
@ -267,6 +270,8 @@ public:
VkQueue get_graphics_queue() const; VkQueue get_graphics_queue() const;
uint32_t get_graphics_queue_family_index() const; uint32_t get_graphics_queue_family_index() const;
static void set_vulkan_hooks(VulkanHooks *p_vulkan_hooks) { vulkan_hooks = p_vulkan_hooks; };
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
int window_get_width(DisplayServer::WindowID p_window = 0); int window_get_width(DisplayServer::WindowID p_window = 0);
int window_get_height(DisplayServer::WindowID p_window = 0); int window_get_height(DisplayServer::WindowID p_window = 0);

View File

@ -0,0 +1,48 @@
/*************************************************************************/
/* vulkan_hooks.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef VULKAN_HOOKS_H
#define VULKAN_HOOKS_H
#ifdef USE_VOLK
#include <volk.h>
#else
#include <vulkan/vulkan.h>
#endif
class VulkanHooks {
public:
virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) { return false; };
virtual bool get_physical_device(VkPhysicalDevice *r_device) { return false; };
virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) { return false; };
virtual ~VulkanHooks(){};
};
#endif

View File

@ -1963,6 +1963,10 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
return OK; return OK;
} }
String Main::get_rendering_driver_name() {
return rendering_driver;
}
// everything the main loop needs to know about frame timings // everything the main loop needs to know about frame timings
static MainTimerSync main_timer_sync; static MainTimerSync main_timer_sync;

View File

@ -49,6 +49,7 @@ public:
static int test_entrypoint(int argc, char *argv[], bool &tests_need_run); static int test_entrypoint(int argc, char *argv[], bool &tests_need_run);
static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true); static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true);
static Error setup2(Thread::ID p_main_tid_override = 0); static Error setup2(Thread::ID p_main_tid_override = 0);
static String get_rendering_driver_name();
#ifdef TESTS_ENABLED #ifdef TESTS_ENABLED
static Error test_setup(); static Error test_setup();
static void test_cleanup(); static void test_cleanup();

87
modules/openxr/SCsub Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_openxr = env_modules.Clone()
#################################################
# Add in our Khronos OpenXR loader
thirdparty_obj = []
thirdparty_dir = "#thirdparty/openxr"
env_openxr.Prepend(
CPPPATH=[
thirdparty_dir,
thirdparty_dir + "/include",
thirdparty_dir + "/src",
thirdparty_dir + "/src/common",
thirdparty_dir + "/src/external/jsoncpp/include",
thirdparty_dir + "/src/loader",
]
)
# may need to check and set:
# - XR_USE_TIMESPEC
env_thirdparty = env_openxr.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
if env["platform"] == "android":
# may need to set OPENXR_ANDROID_VERSION_SUFFIX
env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
# may need to include java parts of the openxr loader
elif env["platform"] == "linuxbsd":
env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX", "XR_USE_PLATFORM_XLIB"])
# FIXME: Review what needs to be set for Android and macOS.
env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
# add in common files (hope these don't clash with us)
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/object_info.cpp")
# add in external jsoncpp dependency
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_value.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
# add in load
if env["platform"] == "android":
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/android_utilities.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
env.modules_sources += thirdparty_obj
#################################################
# And include our module source
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env_openxr.add_source_files(module_obj, "action_map/*.cpp")
# We're a little more targetted with our extensions
if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "extensions/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View File

@ -0,0 +1,91 @@
/*************************************************************************/
/* openxr_action.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_action.h"
void OpenXRAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_localized_name", "localized_name"), &OpenXRAction::set_localized_name);
ClassDB::bind_method(D_METHOD("get_localized_name"), &OpenXRAction::get_localized_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "localized_name"), "set_localized_name", "get_localized_name");
ClassDB::bind_method(D_METHOD("set_action_type", "action_type"), &OpenXRAction::set_action_type);
ClassDB::bind_method(D_METHOD("get_action_type"), &OpenXRAction::get_action_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_type", PROPERTY_HINT_ENUM, "bool,float,vector2,pose"), "set_action_type", "get_action_type");
ClassDB::bind_method(D_METHOD("set_toplevel_paths", "toplevel_paths"), &OpenXRAction::set_toplevel_paths);
ClassDB::bind_method(D_METHOD("get_toplevel_paths"), &OpenXRAction::get_toplevel_paths);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "toplevel_paths", PROPERTY_HINT_ARRAY_TYPE, "STRING"), "set_toplevel_paths", "get_toplevel_paths");
BIND_ENUM_CONSTANT(OPENXR_ACTION_BOOL);
BIND_ENUM_CONSTANT(OPENXR_ACTION_FLOAT);
BIND_ENUM_CONSTANT(OPENXR_ACTION_VECTOR2);
BIND_ENUM_CONSTANT(OPENXR_ACTION_POSE);
}
Ref<OpenXRAction> OpenXRAction::new_action(const char *p_name, const char *p_localized_name, const ActionType p_action_type, const char *p_toplevel_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRAction> action;
action.instantiate();
action->set_name(String(p_name));
action->set_localized_name(String(p_localized_name));
action->set_action_type(p_action_type);
action->parse_toplevel_paths(String(p_toplevel_paths));
return action;
}
void OpenXRAction::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
}
String OpenXRAction::get_localized_name() const {
return localized_name;
}
void OpenXRAction::set_action_type(const OpenXRAction::ActionType p_action_type) {
action_type = p_action_type;
}
OpenXRAction::ActionType OpenXRAction::get_action_type() const {
return action_type;
}
void OpenXRAction::set_toplevel_paths(const PackedStringArray p_toplevel_paths) {
toplevel_paths = p_toplevel_paths;
}
PackedStringArray OpenXRAction::get_toplevel_paths() const {
return toplevel_paths;
}
void OpenXRAction::parse_toplevel_paths(const String p_toplevel_paths) {
toplevel_paths = p_toplevel_paths.split(",", false);
}

View File

@ -0,0 +1,74 @@
/*************************************************************************/
/* openxr_action.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_H
#define OPENXR_ACTION_H
#include "core/io/resource.h"
class OpenXRAction : public Resource {
GDCLASS(OpenXRAction, Resource);
public:
enum ActionType {
OPENXR_ACTION_BOOL,
OPENXR_ACTION_FLOAT,
OPENXR_ACTION_VECTOR2,
OPENXR_ACTION_POSE,
OPENXR_ACTION_HAPTIC,
};
private:
String localized_name;
ActionType action_type = OPENXR_ACTION_FLOAT;
PackedStringArray toplevel_paths;
protected:
static void _bind_methods();
public:
static Ref<OpenXRAction> new_action(const char *p_name, const char *p_localized_name, const ActionType p_action_type, const char *p_toplevel_paths);
void set_localized_name(const String p_localized_name);
String get_localized_name() const;
void set_action_type(const ActionType p_action_type);
ActionType get_action_type() const;
void set_toplevel_paths(const PackedStringArray p_toplevel_paths);
PackedStringArray get_toplevel_paths() const;
void parse_toplevel_paths(const String p_toplevel_paths);
};
VARIANT_ENUM_CAST(OpenXRAction::ActionType);
#endif // !OPENXR_ACTION_H

View File

@ -0,0 +1,261 @@
/*************************************************************************/
/* openxr_action_map.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_action_map.h"
void OpenXRActionMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action_sets", "action_sets"), &OpenXRActionMap::set_action_sets);
ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRActionMap::get_action_sets);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "action_sets", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRActionSet", PROPERTY_USAGE_NO_EDITOR), "set_action_sets", "get_action_sets");
ClassDB::bind_method(D_METHOD("add_action_set", "action_set"), &OpenXRActionMap::add_action_set);
ClassDB::bind_method(D_METHOD("remove_action_set", "action_set"), &OpenXRActionMap::remove_action_set);
ClassDB::bind_method(D_METHOD("set_interaction_profiles", "interaction_profiles"), &OpenXRActionMap::set_interaction_profiles);
ClassDB::bind_method(D_METHOD("get_interaction_profiles"), &OpenXRActionMap::get_interaction_profiles);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "interaction_profiles", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRInteractionProfile", PROPERTY_USAGE_NO_EDITOR), "set_interaction_profiles", "get_interaction_profiles");
ClassDB::bind_method(D_METHOD("add_interaction_profile", "interaction_profile"), &OpenXRActionMap::add_interaction_profile);
ClassDB::bind_method(D_METHOD("remove_interaction_profile", "interaction_profile"), &OpenXRActionMap::remove_interaction_profile);
ClassDB::bind_method(D_METHOD("create_default_action_sets"), &OpenXRActionMap::create_default_action_sets);
}
void OpenXRActionMap::set_action_sets(Array p_action_sets) {
action_sets = p_action_sets;
}
Array OpenXRActionMap::get_action_sets() const {
return action_sets;
}
void OpenXRActionMap::add_action_set(Ref<OpenXRActionSet> p_action_set) {
ERR_FAIL_COND(p_action_set.is_null());
if (action_sets.find(p_action_set) == -1) {
action_sets.push_back(p_action_set);
}
}
void OpenXRActionMap::remove_action_set(Ref<OpenXRActionSet> p_action_set) {
int idx = action_sets.find(p_action_set);
if (idx != -1) {
action_sets.remove_at(idx);
}
}
void OpenXRActionMap::set_interaction_profiles(Array p_interaction_profiles) {
interaction_profiles = p_interaction_profiles;
}
Array OpenXRActionMap::get_interaction_profiles() const {
return interaction_profiles;
}
void OpenXRActionMap::add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
ERR_FAIL_COND(p_interaction_profile.is_null());
if (interaction_profiles.find(p_interaction_profile) == -1) {
interaction_profiles.push_back(p_interaction_profile);
}
}
void OpenXRActionMap::remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile) {
int idx = interaction_profiles.find(p_interaction_profile);
if (idx != -1) {
interaction_profiles.remove_at(idx);
}
}
void OpenXRActionMap::create_default_action_sets() {
// Note, if you make changes here make sure to delete your default_action_map.tres file of it will load an old version.
// Create our Godot action set
Ref<OpenXRActionSet> action_set = OpenXRActionSet::new_action_set("godot", "Godot action set");
add_action_set(action_set);
// Create our actions
Ref<OpenXRAction> trigger = action_set->add_new_action("trigger", "Trigger", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_click = action_set->add_new_action("trigger_click", "Trigger click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> primary_touch = action_set->add_new_action("primary_touch", "Primary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary = action_set->add_new_action("secondary", "Secondary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary_click = action_set->add_new_action("secondary_click", "Secondary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> secondary_touch = action_set->add_new_action("secondary_touch", "Secondary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> menu_button = action_set->add_new_action("menu_button", "Menu button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> select_button = action_set->add_new_action("select_button", "Select button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> ax_button = action_set->add_new_action("ax_button", "A/X button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> ax_touch = action_set->add_new_action("ax_touch", "A/X touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_button = action_set->add_new_action("by_button", "B/Y button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_touch = action_set->add_new_action("by_touch", "B/Y touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC, "/user/hand/left,/user/hand/right");
// Create our interaction profiles
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click");
// generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Vive controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
// wmr controller has no a/b/x/y buttons
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our vive controller is our trackpad
profile->add_new_binding(primary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
// vive controllers have no secondary input
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our WMR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
// wmr controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// wmr controller has no a/b/x/y buttons
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // OpenXR will conver float to bool
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click"); // OpenXR will convert bool to float
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/click,/user/hand/right/input/squeeze/click");
// primary on our wmr controller is our thumbstick, no touch
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
// secondary on our wmr controller is our trackpad
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our HP MR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
// hpmr controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// hpmr controllers only register click, not touch, on our a/b/x/y buttons
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
// primary on our hpmr controller is our thumbstick
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
// No secondary on our hpmr controller
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Meta touch controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
// touch controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
// primary on our touch controller is our thumbstick
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// touch controller has no secondary input
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
// Create our Valve index controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
// index controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers
profile->add_new_binding(ax_touch, "/user/hand/left/input/a/touch,/user/hand/right/input/a/touch");
profile->add_new_binding(by_button, "/user/hand/left/input/b/click,/user/hand/right/input/b/click"); // b on both controllers
profile->add_new_binding(by_touch, "/user/hand/left/input/b/touch,/user/hand/right/input/b/touch");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion
// primary on our index controller is our thumbstick
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
// secondary on our index controller is our trackpad
profile->add_new_binding(secondary, "/user/hand/left/input/trackpad,/user/hand/right/input/trackpad");
profile->add_new_binding(secondary_click, "/user/hand/left/input/trackpad/force,/user/hand/right/input/trackpad/force"); // not sure if this will work but doesn't seem to support click...
profile->add_new_binding(secondary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
}
void OpenXRActionMap::create_editor_action_sets() {
// TODO implement
}
OpenXRActionMap::~OpenXRActionMap() {
action_sets.clear();
interaction_profiles.clear();
}

View File

@ -0,0 +1,68 @@
/*************************************************************************/
/* openxr_action_map.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_SETS_H
#define OPENXR_ACTION_SETS_H
#include "core/io/resource.h"
#include "openxr_action_set.h"
#include "openxr_interaction_profile.h"
class OpenXRActionMap : public Resource {
GDCLASS(OpenXRActionMap, Resource);
private:
Array action_sets;
Array interaction_profiles;
protected:
static void _bind_methods();
public:
void set_action_sets(Array p_action_sets);
Array get_action_sets() const;
void add_action_set(Ref<OpenXRActionSet> p_action_set);
void remove_action_set(Ref<OpenXRActionSet> p_action_set);
void set_interaction_profiles(Array p_interaction_profiles);
Array get_interaction_profiles() const;
void add_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile);
void remove_interaction_profile(Ref<OpenXRInteractionProfile> p_interaction_profile);
void create_default_action_sets();
void create_editor_action_sets();
~OpenXRActionMap();
};
#endif // !OPENXR_ACTION_SETS_H

View File

@ -0,0 +1,111 @@
/*************************************************************************/
/* openxr_action_set.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_action_set.h"
void OpenXRActionSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_localized_name", "localized_name"), &OpenXRActionSet::set_localized_name);
ClassDB::bind_method(D_METHOD("get_localized_name"), &OpenXRActionSet::get_localized_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "localized_name"), "set_localized_name", "get_localized_name");
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &OpenXRActionSet::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &OpenXRActionSet::get_priority);
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority"), "set_priority", "get_priority");
ClassDB::bind_method(D_METHOD("set_actions", "actions"), &OpenXRActionSet::set_actions);
ClassDB::bind_method(D_METHOD("get_actions"), &OpenXRActionSet::get_actions);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "actions", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRAction", PROPERTY_USAGE_NO_EDITOR), "set_actions", "get_actions");
ClassDB::bind_method(D_METHOD("add_action", "action"), &OpenXRActionSet::add_action);
ClassDB::bind_method(D_METHOD("remove_action", "action"), &OpenXRActionSet::remove_action);
}
Ref<OpenXRActionSet> OpenXRActionSet::new_action_set(const char *p_name, const char *p_localized_name, const int p_priority) {
// This is a helper function to help build our default action sets
Ref<OpenXRActionSet> action_set;
action_set.instantiate();
action_set->set_name(String(p_name));
action_set->set_localized_name(p_localized_name);
action_set->set_priority(p_priority);
return action_set;
}
void OpenXRActionSet::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
}
String OpenXRActionSet::get_localized_name() const {
return localized_name;
}
void OpenXRActionSet::set_priority(const int p_priority) {
priority = p_priority;
}
int OpenXRActionSet::get_priority() const {
return priority;
}
void OpenXRActionSet::set_actions(Array p_actions) {
actions = p_actions;
}
Array OpenXRActionSet::get_actions() const {
return actions;
}
void OpenXRActionSet::add_action(Ref<OpenXRAction> p_action) {
ERR_FAIL_COND(p_action.is_null());
if (actions.find(p_action) == -1) {
actions.push_back(p_action);
}
}
void OpenXRActionSet::remove_action(Ref<OpenXRAction> p_action) {
int idx = actions.find(p_action);
if (idx != -1) {
actions.remove_at(idx);
}
}
Ref<OpenXRAction> OpenXRActionSet::add_new_action(const char *p_name, const char *p_localized_name, const OpenXRAction::ActionType p_action_type, const char *p_toplevel_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRAction> new_action = OpenXRAction::new_action(p_name, p_localized_name, p_action_type, p_toplevel_paths);
add_action(new_action);
return new_action;
}
OpenXRActionSet::~OpenXRActionSet() {
actions.clear();
}

View File

@ -0,0 +1,70 @@
/*************************************************************************/
/* openxr_action_set.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_ACTION_SET_H
#define OPENXR_ACTION_SET_H
#include "core/io/resource.h"
#include "openxr_action.h"
class OpenXRActionSet : public Resource {
GDCLASS(OpenXRActionSet, Resource);
private:
String localized_name;
int priority = 0;
Array actions;
protected:
static void _bind_methods();
public:
static Ref<OpenXRActionSet> new_action_set(const char *p_name, const char *p_localized_name, const int p_priority = 0);
void set_localized_name(const String p_localized_name);
String get_localized_name() const;
void set_priority(const int p_priority);
int get_priority() const;
void set_actions(Array p_actions);
Array get_actions() const;
void add_action(Ref<OpenXRAction> p_action);
void remove_action(Ref<OpenXRAction> p_action);
Ref<OpenXRAction> add_new_action(const char *p_name, const char *p_localized_name, const OpenXRAction::ActionType p_action_type, const char *p_toplevel_paths);
~OpenXRActionSet();
};
#endif // !OPENXR_ACTION_SET_H

View File

@ -0,0 +1,136 @@
/*************************************************************************/
/* openxr_interaction_profile.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_interaction_profile.h"
void OpenXRIPBinding::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_action", "action"), &OpenXRIPBinding::set_action);
ClassDB::bind_method(D_METHOD("get_action"), &OpenXRIPBinding::get_action);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "action", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRAction"), "set_action", "get_action");
ClassDB::bind_method(D_METHOD("set_paths", "paths"), &OpenXRIPBinding::set_paths);
ClassDB::bind_method(D_METHOD("get_paths"), &OpenXRIPBinding::get_paths);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "paths", PROPERTY_HINT_ARRAY_TYPE, "STRING"), "set_paths", "get_paths");
}
Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_action, const char *p_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRIPBinding> binding;
binding.instantiate();
binding->set_action(p_action);
binding->parse_paths(String(p_paths));
return binding;
}
void OpenXRIPBinding::set_action(const Ref<OpenXRAction> p_action) {
action = p_action;
}
Ref<OpenXRAction> OpenXRIPBinding::get_action() const {
return action;
}
void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) {
paths = p_paths;
}
PackedStringArray OpenXRIPBinding::get_paths() const {
return paths;
}
void OpenXRIPBinding::parse_paths(const String p_paths) {
paths = p_paths.split(",", false);
}
OpenXRIPBinding::~OpenXRIPBinding() {
action.unref();
}
void OpenXRInteractionProfile::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_interaction_profile_path", "interaction_profile_path"), &OpenXRInteractionProfile::set_interaction_profile_path);
ClassDB::bind_method(D_METHOD("get_interaction_profile_path"), &OpenXRInteractionProfile::get_interaction_profile_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "interaction_profile_path"), "set_interaction_profile_path", "get_interaction_profile_path");
ClassDB::bind_method(D_METHOD("set_bindings", "bindings"), &OpenXRInteractionProfile::set_bindings);
ClassDB::bind_method(D_METHOD("get_bindings"), &OpenXRInteractionProfile::get_bindings);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bindings", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRIPBinding", PROPERTY_USAGE_NO_EDITOR), "set_bindings", "get_bindings");
}
Ref<OpenXRInteractionProfile> OpenXRInteractionProfile::new_profile(const char *p_input_profile_path) {
Ref<OpenXRInteractionProfile> profile;
profile.instantiate();
profile->set_interaction_profile_path(String(p_input_profile_path));
return profile;
}
void OpenXRInteractionProfile::set_interaction_profile_path(const String p_input_profile_path) {
interaction_profile_path = p_input_profile_path;
}
String OpenXRInteractionProfile::get_interaction_profile_path() const {
return interaction_profile_path;
}
void OpenXRInteractionProfile::set_bindings(Array p_bindings) {
bindings = p_bindings;
}
Array OpenXRInteractionProfile::get_bindings() const {
return bindings;
}
void OpenXRInteractionProfile::add_binding(Ref<OpenXRIPBinding> p_binding) {
ERR_FAIL_COND(p_binding.is_null());
if (bindings.find(p_binding) == -1) {
bindings.push_back(p_binding);
}
}
void OpenXRInteractionProfile::remove_binding(Ref<OpenXRIPBinding> p_binding) {
int idx = bindings.find(p_binding);
if (idx != -1) {
bindings.remove_at(idx);
}
}
void OpenXRInteractionProfile::add_new_binding(const Ref<OpenXRAction> p_action, const char *p_paths) {
// This is a helper function to help build our default action sets
Ref<OpenXRIPBinding> binding = OpenXRIPBinding::new_binding(p_action, p_paths);
add_binding(binding);
}
OpenXRInteractionProfile::~OpenXRInteractionProfile() {
bindings.clear();
}

View File

@ -0,0 +1,89 @@
/*************************************************************************/
/* openxr_interaction_profile.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_INTERACTION_PROFILE_H
#define OPENXR_INTERACTION_PROFILE_H
#include "core/io/resource.h"
#include "openxr_action.h"
class OpenXRIPBinding : public Resource {
GDCLASS(OpenXRIPBinding, Resource);
private:
Ref<OpenXRAction> action;
PackedStringArray paths;
protected:
static void _bind_methods();
public:
static Ref<OpenXRIPBinding> new_binding(const Ref<OpenXRAction> p_action, const char *p_paths);
void set_action(const Ref<OpenXRAction> p_action);
Ref<OpenXRAction> get_action() const;
void set_paths(const PackedStringArray p_paths);
PackedStringArray get_paths() const;
void parse_paths(const String p_paths);
~OpenXRIPBinding();
};
class OpenXRInteractionProfile : public Resource {
GDCLASS(OpenXRInteractionProfile, Resource);
private:
String interaction_profile_path;
Array bindings;
protected:
static void _bind_methods();
public:
static Ref<OpenXRInteractionProfile> new_profile(const char *p_input_profile_path);
void set_interaction_profile_path(const String p_input_profile_path);
String get_interaction_profile_path() const;
void set_bindings(Array p_bindings);
Array get_bindings() const;
void add_binding(Ref<OpenXRIPBinding> p_binding);
void remove_binding(Ref<OpenXRIPBinding> p_binding);
void add_new_binding(const Ref<OpenXRAction> p_action, const char *p_paths);
~OpenXRInteractionProfile();
};
#endif // !OPENXR_INTERACTION_PROFILE_H

27
modules/openxr/config.py Normal file
View File

@ -0,0 +1,27 @@
def can_build(env, platform):
if (
platform == "linuxbsd" or platform == "windows"
): # or platform == "android" -- temporarily disabled android support
return env["openxr"]
else:
# not supported on these platforms
return False
def configure(env):
pass
def get_doc_classes():
return [
"OpenXRInterface",
"OpenXRAction",
"OpenXRActionSet",
"OpenXRActionMap",
"OpenXRInteractionProfile",
"OpenXRIPBinding",
]
def get_doc_path():
return "doc_classes"

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRAction" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR action.
</brief_description>
<description>
This resource defines an OpenXR action. Actions can be used both for inputs (buttons/joystick/trigger/etc) and outputs (haptics).
OpenXR performs automatic conversion between action type and input type whenever possible. An analogue trigger bound to a boolean action will thus return [code]false[/core] if the trigger is depressed and [code]true[/code] if pressed fully.
Actions are not directly bound to specific devices, instead OpenXR recognises a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths.
Note that the name of the resource is used to register the action with.
</description>
<tutorials>
</tutorials>
<members>
<member name="action_type" type="int" setter="set_action_type" getter="get_action_type" enum="OpenXRAction.ActionType" default="1">
The type of action.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
The localised description of this action.
</member>
<member name="toplevel_paths" type="PackedStringArray" setter="set_toplevel_paths" getter="get_toplevel_paths" default="PackedStringArray()">
A collections of toplevel paths to which this action can be bound.
</member>
</members>
<constants>
<constant name="OPENXR_ACTION_BOOL" value="0" enum="ActionType">
This action provides a boolean value.
</constant>
<constant name="OPENXR_ACTION_FLOAT" value="1" enum="ActionType">
This action provides a float value between [code]0.0[/code] and [code]1.0[/code] for any analogue input such as triggers.
</constant>
<constant name="OPENXR_ACTION_VECTOR2" value="2" enum="ActionType">
This action provides a vector2 value and can be bound to embedded trackpads and joysticks
</constant>
<constant name="OPENXR_ACTION_POSE" value="3" enum="ActionType">
</constant>
</constants>
</class>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRActionMap" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Collection of [OpenXRActionSet] and [OpenXRInteractionProfile] resources for the OpenXR module.
</brief_description>
<description>
OpenXR uses an action system similar to Godots Input map system to bind inputs and outputs on various types of XR controllers to named actions. OpenXR specifies more detail on these inputs and outputs than Godot supports.
Another important distinction is that OpenXR offers no control over these bindings. The bindings we register are suggestions, it is up to the XR runtime to offer users the ability to change these bindings. This allows the XR runtime to fill in the gaps if new hardware becomes available.
The action map therefor needs to be loaded at startup and can't be changed afterwards. This resource is a container for the entire action map.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_action_set">
<return type="void" />
<argument index="0" name="action_set" type="OpenXRActionSet" />
<description>
Add an action set.
</description>
</method>
<method name="add_interaction_profile">
<return type="void" />
<argument index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Add an interaction profile.
</description>
</method>
<method name="create_default_action_sets">
<return type="void" />
<description>
Setup this action set with our default actions.
</description>
</method>
<method name="remove_action_set">
<return type="void" />
<argument index="0" name="action_set" type="OpenXRActionSet" />
<description>
Remove an action set.
</description>
</method>
<method name="remove_interaction_profile">
<return type="void" />
<argument index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Remove an interaction profile.
</description>
</method>
</methods>
<members>
<member name="action_sets" type="Array" setter="set_action_sets" getter="get_action_sets" default="[]">
</member>
<member name="interaction_profiles" type="Array" setter="set_interaction_profiles" getter="get_interaction_profiles" default="[]">
</member>
</members>
</class>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRActionSet" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Collection of [OpenXRAction] resources that make up an action set.
</brief_description>
<description>
Action sets in OpenXR define a collection of actions that can be activated in unison. This allows games to easily change between different states that require different inputs or need to reinterpret inputs. For instance we could have an action set that is active when a menu is open, an action set that is active when the player is freely walking around and an action set that is active when the player is controlling a vehicle.
Action sets can contain the same actions, or actions with the same name, if such action sets are active at the same time the action set with the highest priority defines which binding is active.
Note that the name of the resource is used to identify the action set within OpenXR.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_action">
<return type="void" />
<argument index="0" name="action" type="OpenXRAction" />
<description>
Add an action to this action set.
</description>
</method>
<method name="remove_action">
<return type="void" />
<argument index="0" name="action" type="OpenXRAction" />
<description>
Remove an action from this action set.
</description>
</method>
</methods>
<members>
<member name="actions" type="Array" setter="set_actions" getter="get_actions" default="[]">
Collection of actions for this action set.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
The localised name of this action set.
</member>
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="0">
The priority for this action set.
</member>
</members>
</class>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRIPBinding" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Defines a binding between an [OpenXRAction] and an XR input or output.
</brief_description>
<description>
This binding resource binds an OpenXR action to inputs or outputs. As most controllers have left hand and right versions that are handled by the same interaction profile we can specify multiple bindings. For instance an action "Fire" could be bound to both "/user/hand/left/input/trigger" and "/user/hand/right/input/trigger".
</description>
<tutorials>
</tutorials>
<members>
<member name="action" type="OpenXRAction" setter="set_action" getter="get_action">
Action that is bound to these paths.
</member>
<member name="paths" type="PackedStringArray" setter="set_paths" getter="get_paths" default="PackedStringArray()">
Paths that define the inputs or outputs bound on the device.
</member>
</members>
</class>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInteractionProfile" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Suggested bindings object for OpenXR.
</brief_description>
<description>
This object stores suggested bindings for an interaction profile. Interaction profiles define the meta data for a tracked XR device such as an XR controller.
For more information see the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles]interaction profiles info in the OpenXR specification[/url].
</description>
<tutorials>
</tutorials>
<members>
<member name="bindings" type="Array" setter="set_bindings" getter="get_bindings" default="[]">
Action bindings for this interaction profile.
</member>
<member name="interaction_profile_path" type="String" setter="set_interaction_profile_path" getter="get_interaction_profile_path" default="&quot;&quot;">
The interaction profile path identifying the XR device.
</member>
</members>
</class>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRInterface" inherits="XRInterface" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Our OpenXR interface.
</brief_description>
<description>
The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games.
Due to the needs of OpenXR this interface works slightly different then other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
</description>
<tutorials>
<link title="OpenXR documentation">$DOCS_URL/tutorials/vr/openxr/index.html</link>
</tutorials>
</class>

View File

@ -0,0 +1,71 @@
/*************************************************************************/
/* openxr_android_extension.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_android_extension.h"
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
OpenXRAndroidExtension *OpenXRAndroidExtension::singleton = nullptr;
OpenXRAndroidExtension *OpenXRAndroidExtension::get_singleton() {
return singleton;
}
OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
OpenXRExtensionWrapper(p_openxr_api) {
singleton = this;
request_extensions[XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME] = nullptr; // must be available
// Initialize the loader
PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
result = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction *)(&xrInitializeLoaderKHR));
ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to retrieve pointer to xrInitializeLoaderKHR");
// TODO fix this code, this is still code from GDNative!
JNIEnv *env = android_api->godot_android_get_env();
JavaVM *vm;
env->GetJavaVM(&vm);
jobject activity_object = env->NewGlobalRef(android_api->godot_android_get_activity());
XrLoaderInitInfoAndroidKHR loader_init_info_android = {
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
.next = nullptr,
.applicationVM = vm,
.applicationContext = activity_object
};
xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to call xrInitializeLoaderKHR");
}
OpenXRAndroidExtension::~OpenXRAndroidExtension() {
singleton = nullptr;
}

View File

@ -0,0 +1,47 @@
/*************************************************************************/
/* openxr_android_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_ANDROID_EXTENSION_H
#define OPENXR_ANDROID_EXTENSION_H
#include "openxr_extension_wrapper.h"
class OpenXRAndroidExtension : public OpenXRExtensionWrapper {
public:
static OpenXRAndroidExtension *get_singleton();
OpenXRAndroidExtension(OpenXRAPI *p_openxr_api);
virtual ~OpenXRAndroidExtension() override;
private:
static OpenXRAndroidExtension *singleton;
};
#endif // !OPENXR_ANDROID_EXTENSION_H

View File

@ -0,0 +1,45 @@
/*************************************************************************/
/* openxr_composition_layer_provider.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_PROVIDER_H
#define OPENXR_COMPOSITION_LAYER_PROVIDER_H
#include <openxr/openxr.h>
// Interface for OpenXR extensions that provide a composition layer.
class OpenXRCompositionLayerProvider {
public:
// TODO changed to normal method definition for now
// CI complains until we implement this, haven't ported it yet from plugin
// virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0;
XrCompositionLayerBaseHeader *get_composition_layer() { return nullptr; };
};
#endif // OPENXR_COMPOSITION_LAYER_PROVIDER_H

View File

@ -0,0 +1,106 @@
/*************************************************************************/
/* openxr_extension_wrapper.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_EXTENSION_WRAPPER_H
#define OPENXR_EXTENSION_WRAPPER_H
#include "core/error/error_macros.h"
#include "core/math/camera_matrix.h"
#include "core/templates/map.h"
#include "core/templates/rid.h"
#include "thirdparty/openxr/src/common/xr_linear.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRExtensionWrapper {
protected:
OpenXRAPI *openxr_api;
// Store extension we require.
// If bool pointer is a nullptr this means this extension is mandatory and initialisation will fail if it is not available
// If bool pointer is set, value will be set to true or false depending on whether extension is available
Map<const char *, bool *> request_extensions;
public:
virtual Map<const char *, bool *> get_request_extensions() {
return request_extensions;
}
// These functions allow an extension to add entries to a struct chain.
// `p_next_pointer` points to the last struct that was created for this chain
// and should be used as the value for the `pNext` pointer in the first struct you add.
// You should return the pointer to the last struct you define as your result.
// If you are not adding any structs, just return `p_next_pointer`.
// See existing extensions for examples of this implementation.
virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
virtual void on_instance_created(const XrInstance p_instance) {}
virtual void on_instance_destroyed() {}
virtual void on_session_created(const XrSession p_instance) {}
virtual void on_process() {}
virtual void on_pre_render() {}
virtual void on_session_destroyed() {}
virtual void on_state_idle() {}
virtual void on_state_ready() {}
virtual void on_state_synchronized() {}
virtual void on_state_visible() {}
virtual void on_state_focused() {}
virtual void on_state_stopping() {}
virtual void on_state_loss_pending() {}
virtual void on_state_exiting() {}
// Returns true if the event was handled, false otherwise.
virtual bool on_event_polled(const XrEventDataBuffer &event) {
return false;
}
OpenXRExtensionWrapper(OpenXRAPI *p_openxr_api) { openxr_api = p_openxr_api; };
virtual ~OpenXRExtensionWrapper() = default;
};
class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper {
public:
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0;
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0;
virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) = 0;
virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) = 0;
OpenXRGraphicsExtensionWrapper(OpenXRAPI *p_openxr_api) :
OpenXRExtensionWrapper(p_openxr_api){};
};
#endif // ~OPENXR_EXTENSION_WRAPPER_H

View File

@ -0,0 +1,695 @@
/*************************************************************************/
/* openxr_vulkan_extension.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "core/string/print_string.h"
#include "modules/openxr/extensions/openxr_vulkan_extension.h"
#include "modules/openxr/openxr_api.h"
#include "modules/openxr/openxr_util.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
// need to include Vulkan so we know of type definitions
#define XR_USE_GRAPHICS_API_VULKAN
#ifdef WINDOWS_ENABLED
// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
// however due to the way the openxr headers are put together, we have no choice.
#include <windows.h>
#endif
// include platform dependent structs
#include <openxr/openxr_platform.h>
PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR_ptr = nullptr;
PFN_xrCreateVulkanInstanceKHR xrCreateVulkanInstanceKHR_ptr = nullptr;
PFN_xrGetVulkanGraphicsDevice2KHR xrGetVulkanGraphicsDevice2KHR_ptr = nullptr;
PFN_xrCreateVulkanDeviceKHR xrCreateVulkanDeviceKHR_ptr = nullptr;
OpenXRVulkanExtension::OpenXRVulkanExtension(OpenXRAPI *p_openxr_api) :
OpenXRGraphicsExtensionWrapper(p_openxr_api) {
VulkanContext::set_vulkan_hooks(this);
request_extensions[XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME] = nullptr; // must be available
ERR_FAIL_NULL(openxr_api);
}
OpenXRVulkanExtension::~OpenXRVulkanExtension() {
VulkanContext::set_vulkan_hooks(nullptr);
}
void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
XrResult result;
ERR_FAIL_NULL(openxr_api);
// Obtain pointers to functions we're accessing here, they are (not yet) part of core.
result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsRequirements2KHR_ptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to xrGetVulkanGraphicsRequirements2KHR entry point [", openxr_api->get_error_string(result), "]");
}
result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanInstanceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanInstanceKHR_ptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to xrCreateVulkanInstanceKHR entry point [", openxr_api->get_error_string(result), "]");
}
result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsDevice2KHR_ptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to xrGetVulkanGraphicsDevice2KHR entry point [", openxr_api->get_error_string(result), "]");
}
result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanDeviceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanDeviceKHR_ptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to xrCreateVulkanDeviceKHR entry point [", openxr_api->get_error_string(result), "]");
}
}
XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements) {
ERR_FAIL_NULL_V(xrGetVulkanGraphicsRequirements2KHR_ptr, XR_ERROR_HANDLE_INVALID);
return (*xrGetVulkanGraphicsRequirements2KHR_ptr)(p_instance, p_system_id, p_graphics_requirements);
}
bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_version) {
ERR_FAIL_NULL_V(openxr_api, false);
XrGraphicsRequirementsVulkan2KHR vulkan_requirements = {
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR, // type
nullptr, // next
0, // minApiVersionSupported
0 // maxApiVersionSupported
};
XrResult result = xrGetVulkanGraphicsRequirements2KHR(openxr_api->get_instance(), openxr_api->get_system_id(), &vulkan_requirements);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get vulkan graphics requirements [", openxr_api->get_error_string(result), "]");
return false;
}
// #ifdef DEBUG
print_line("OpenXR: XrGraphicsRequirementsVulkan2KHR:");
print_line(" - minApiVersionSupported: ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line(" - maxApiVersionSupported: ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
// #endif
if (p_desired_version < vulkan_requirements.minApiVersionSupported) {
print_line("OpenXR: Requested Vulkan version does not meet the minimum version this runtime supports.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
return false;
}
if (p_desired_version > vulkan_requirements.maxApiVersionSupported) {
print_line("OpenXR: Requested Vulkan version exceeds the maximum version this runtime has been tested on and is known to support.");
print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.minApiVersionSupported));
print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(vulkan_requirements.maxApiVersionSupported));
}
return true;
}
XrResult OpenXRVulkanExtension::xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result) {
ERR_FAIL_NULL_V(xrCreateVulkanInstanceKHR_ptr, XR_ERROR_HANDLE_INVALID);
return (*xrCreateVulkanInstanceKHR_ptr)(p_instance, p_create_info, r_vulkan_instance, r_vulkan_result);
}
bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) {
// get the vulkan version we are creating
uint32_t vulkan_version = p_vulkan_create_info->pApplicationInfo->apiVersion;
uint32_t major_version = VK_VERSION_MAJOR(vulkan_version);
uint32_t minor_version = VK_VERSION_MINOR(vulkan_version);
uint32_t patch_version = VK_VERSION_PATCH(vulkan_version);
XrVersion desired_version = XR_MAKE_VERSION(major_version, minor_version, patch_version);
// check if this is supported
if (!check_graphics_api_support(desired_version)) {
return false;
}
XrVulkanInstanceCreateInfoKHR xr_vulkan_instance_info = {
XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, // type
nullptr, // next
openxr_api->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
p_vulkan_create_info, // vulkanCreateInfo
nullptr, // vulkanAllocator
};
VkResult vk_result = VK_SUCCESS;
XrResult result = xrCreateVulkanInstanceKHR(openxr_api->get_instance(), &xr_vulkan_instance_info, &vulkan_instance, &vk_result);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create vulkan instance [", openxr_api->get_error_string(result), "]");
return false;
}
ERR_FAIL_COND_V_MSG(vk_result == VK_ERROR_INCOMPATIBLE_DRIVER, false,
"Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
"vkCreateInstance Failure");
ERR_FAIL_COND_V_MSG(vk_result == VK_ERROR_EXTENSION_NOT_PRESENT, false,
"Cannot find a specified extension library.\n"
"Make sure your layers path is set appropriately.\n"
"vkCreateInstance Failure");
ERR_FAIL_COND_V_MSG(vk_result, false,
"vkCreateInstance failed.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
"Please look at the Getting Started guide for additional information.\n"
"vkCreateInstance Failure");
*r_instance = vulkan_instance;
return true;
}
XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device) {
ERR_FAIL_NULL_V(xrGetVulkanGraphicsDevice2KHR_ptr, XR_ERROR_HANDLE_INVALID);
return (*xrGetVulkanGraphicsDevice2KHR_ptr)(p_instance, p_get_info, r_vulkan_physical_device);
}
bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
ERR_FAIL_NULL_V(openxr_api, false);
XrVulkanGraphicsDeviceGetInfoKHR get_info = {
XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, // type
nullptr, // next
openxr_api->get_system_id(), // systemId
vulkan_instance, // vulkanInstance
};
XrResult result = xrGetVulkanGraphicsDevice2KHR(openxr_api->get_instance(), &get_info, &vulkan_physical_device);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to obtain vulkan physical device [", openxr_api->get_error_string(result), "]");
return false;
}
*r_device = vulkan_physical_device;
return true;
}
XrResult OpenXRVulkanExtension::xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result) {
ERR_FAIL_NULL_V(xrCreateVulkanDeviceKHR_ptr, XR_ERROR_HANDLE_INVALID);
return (*xrCreateVulkanDeviceKHR_ptr)(p_instance, p_create_info, r_device, r_result);
}
bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) {
ERR_FAIL_NULL_V(openxr_api, false);
// the first entry in our queue list should be the one we need to remember...
vulkan_queue_family_index = p_device_create_info->pQueueCreateInfos[0].queueFamilyIndex;
vulkan_queue_index = 0; // ??
XrVulkanDeviceCreateInfoKHR create_info = {
XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, // type
nullptr, // next
openxr_api->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
vulkan_physical_device, // vulkanPhysicalDevice
p_device_create_info, // vulkanCreateInfo
nullptr // vulkanAllocator
};
VkResult vk_result = VK_SUCCESS;
XrResult result = xrCreateVulkanDeviceKHR(openxr_api->get_instance(), &create_info, &vulkan_device, &vk_result);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to create vulkan device [", openxr_api->get_error_string(result), "]");
return false;
}
if (vk_result != VK_SUCCESS) {
print_line("OpenXR: Failed to create vulkan device [vulkan error", vk_result, "]");
}
*r_device = vulkan_device;
return true;
}
XrGraphicsBindingVulkanKHR OpenXRVulkanExtension::graphics_binding_vulkan;
void *OpenXRVulkanExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
graphics_binding_vulkan.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;
graphics_binding_vulkan.next = p_next_pointer;
graphics_binding_vulkan.instance = vulkan_instance;
graphics_binding_vulkan.physicalDevice = vulkan_physical_device;
graphics_binding_vulkan.device = vulkan_device;
graphics_binding_vulkan.queueFamilyIndex = vulkan_queue_family_index;
graphics_binding_vulkan.queueIndex = vulkan_queue_index;
return &graphics_binding_vulkan;
}
void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
// We might want to do more here especially if we keep things in linear color space
// Possibly add in R10G10B10A2 as an option if we're using the mobile renderer.
p_usable_swap_chains.push_back(VK_FORMAT_R8G8B8A8_SRGB);
p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_SRGB);
p_usable_swap_chains.push_back(VK_FORMAT_R8G8B8A8_UINT);
p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_UINT);
}
bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) {
XrSwapchainImageVulkanKHR *images = nullptr;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL_V(rendering_server, false);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL_V(rendering_device, false);
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchaim image count [", openxr_api->get_error_string(result), "]");
return false;
}
images = (XrSwapchainImageVulkanKHR *)memalloc(sizeof(XrSwapchainImageVulkanKHR) * swapchain_length);
ERR_FAIL_NULL_V_MSG(images, false, "OpenXR Couldn't allocate memory for swap chain image");
for (uint64_t i = 0; i < swapchain_length; i++) {
images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;
images[i].next = nullptr;
images[i].image = nullptr;
}
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images);
if (XR_FAILED(result)) {
print_line("OpenXR: Failed to get swapchaim images [", openxr_api->get_error_string(result), "]");
memfree(images);
return false;
}
// SwapchainGraphicsData *data = (SwapchainGraphicsData *)memalloc(sizeof(SwapchainGraphicsData));
SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
if (data == nullptr) {
print_line("OpenXR: Failed to allocate memory for swapchain data");
memfree(images);
return false;
}
*r_swapchain_graphics_data = data;
data->is_multiview = (p_array_size > 1);
RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB; // TODO set this based on p_swapchain_format
RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1; // TODO set this based on p_sample_count
uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
Vector<RID> image_rids;
Vector<RID> framebuffers;
// create Godot texture objects for each entry in our swapchain
for (uint64_t i = 0; i < swapchain_length; i++) {
RID image_rid = rendering_device->texture_create_from_extension(
p_array_size == 1 ? RenderingDevice::TEXTURE_TYPE_2D : RenderingDevice::TEXTURE_TYPE_2D_ARRAY,
format,
samples,
usage_flags,
(uint64_t)images[i].image,
p_width,
p_height,
1,
p_array_size);
image_rids.push_back(image_rid);
{
Vector<RID> fb;
fb.push_back(image_rid);
RID fb_rid = rendering_device->framebuffer_create(fb, RenderingDevice::INVALID_ID, p_array_size);
framebuffers.push_back(fb_rid);
}
}
data->image_rids = image_rids;
data->framebuffers = framebuffers;
memfree(images);
return true;
}
bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems
XrMatrix4x4f matrix;
XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
r_camera_matrix.matrix[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, false);
ERR_FAIL_COND_V(p_from_render_target.is_null(), false);
ERR_FAIL_NULL_V(RendererStorageRD::base_singleton, false);
RID source_image = RendererStorageRD::base_singleton->render_target_get_rd_texture(p_from_render_target);
ERR_FAIL_COND_V(source_image.is_null(), false);
RID depth_image; // TODO implement
ERR_FAIL_INDEX_V(p_image_index, data->framebuffers.size(), false);
RID fb = data->framebuffers[p_image_index];
ERR_FAIL_COND_V(fb.is_null(), false);
// Our vulkan extension can only be used in conjunction with our vulkan renderer.
// We need access to the effects object in order to have access to our copy logic.
// Breaking all the rules but there is no nice way to do this.
EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
ERR_FAIL_NULL_V(effects, false);
effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
return true;
}
void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
if (*p_swapchain_graphics_data == nullptr) {
return;
}
SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
RenderingServer *rendering_server = RenderingServer::get_singleton();
ERR_FAIL_NULL(rendering_server);
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL(rendering_device);
for (int i = 0; i < data->image_rids.size(); i++) {
// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain
rendering_device->free(data->image_rids[i]);
}
data->image_rids.clear();
for (int i = 0; i < data->framebuffers.size(); i++) {
// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain
rendering_device->free(data->framebuffers[i]);
}
data->framebuffers.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
}
#define ENUM_TO_STRING_CASE(e) \
case e: { \
return String(#e); \
} break;
String OpenXRVulkanExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
// This really should be in vulkan_context...
VkFormat format = VkFormat(p_swapchain_format);
switch (format) {
ENUM_TO_STRING_CASE(VK_FORMAT_UNDEFINED)
ENUM_TO_STRING_CASE(VK_FORMAT_R4G4_UNORM_PACK8)
ENUM_TO_STRING_CASE(VK_FORMAT_R4G4B4A4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B4G4R4A4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R5G6B5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B5G6R5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R5G5B5A1_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B5G5R5A1_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_A1R5G5B5_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R8G8B8A8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8A8_SRGB)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A8B8G8R8_SRGB_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2R10G10B10_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_USCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SSCALED_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_UINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_A2B10G10R10_SINT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_USCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SSCALED)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R16G16B16A16_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R32G32B32A32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_SINT)
ENUM_TO_STRING_CASE(VK_FORMAT_R64G64B64A64_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_B10G11R11_UFLOAT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_D16_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_X8_D24_UNORM_PACK32)
ENUM_TO_STRING_CASE(VK_FORMAT_D32_SFLOAT)
ENUM_TO_STRING_CASE(VK_FORMAT_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D16_UNORM_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D24_UNORM_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_D32_SFLOAT_S8_UINT)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGB_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGB_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGBA_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC1_RGBA_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC2_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC2_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC3_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC3_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC4_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC5_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC6H_UFLOAT_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC6H_SFLOAT_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC7_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_BC7_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11G11_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_EAC_R11G11_SNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_UNORM_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_SRGB_BLOCK)
ENUM_TO_STRING_CASE(VK_FORMAT_G8B8G8R8_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B8G8R8G8_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6G10X6_UNORM_2PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4_UNORM_PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4G12X4_UNORM_2PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16)
ENUM_TO_STRING_CASE(VK_FORMAT_G16B16G16R16_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_B16G16R16G16_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_420_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_422_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT)
ENUM_TO_STRING_CASE(VK_FORMAT_MAX_ENUM)
default: {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
} break;
}
}

View File

@ -0,0 +1,93 @@
/*************************************************************************/
/* openxr_vulkan_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_VULKAN_EXTENSION_H
#define OPENXR_VULKAN_EXTENSION_H
#include "core/templates/vector.h"
#include "openxr_extension_wrapper.h"
#include "drivers/vulkan/vulkan_context.h"
// Forward declare these so we don't need OpenXR headers where-ever this is included
// Including OpenXR at this point gives loads and loads of compile issues especially
// on Windows because windows.h is EVIL and really shouldn't be included outside of platform
// but we really don't have a choice in the matter
struct XrGraphicsRequirementsVulkanKHR;
struct XrVulkanInstanceCreateInfoKHR;
struct XrVulkanGraphicsDeviceGetInfoKHR;
struct XrVulkanDeviceCreateInfoKHR;
struct XrGraphicsBindingVulkanKHR;
class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
public:
OpenXRVulkanExtension(OpenXRAPI *p_openxr_api);
virtual ~OpenXRVulkanExtension() override;
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance);
virtual bool get_physical_device(VkPhysicalDevice *r_device);
virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device);
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) override;
virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) override;
private:
static OpenXRVulkanExtension *singleton;
static XrGraphicsBindingVulkanKHR graphics_binding_vulkan; // declaring this as static so we don't need to know its size and we only need it once when creating our session
struct SwapchainGraphicsData {
bool is_multiview;
Vector<RID> image_rids;
Vector<RID> framebuffers;
};
bool check_graphics_api_support(XrVersion p_desired_version);
VkInstance vulkan_instance;
VkPhysicalDevice vulkan_physical_device;
VkDevice vulkan_device;
uint32_t vulkan_queue_family_index;
uint32_t vulkan_queue_index;
XrResult xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements);
XrResult xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result);
XrResult xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device);
XrResult xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result);
};
#endif // !OPENXR_VULKAN_EXTENSION_H

File diff suppressed because it is too large Load Diff

261
modules/openxr/openxr_api.h Normal file
View File

@ -0,0 +1,261 @@
/*************************************************************************/
/* openxr_api.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_DRIVER_H
#define OPENXR_DRIVER_H
#include "core/error/error_macros.h"
#include "core/math/camera_matrix.h"
#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/os/memory.h"
#include "core/string/ustring.h"
#include "core/templates/map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/vector.h"
#include "servers/xr/xr_pose.h"
#include "thirdparty/openxr/src/common/xr_linear.h"
#include <openxr/openxr.h>
#include "action_map/openxr_action.h"
#include "extensions/openxr_composition_layer_provider.h"
#include "extensions/openxr_extension_wrapper.h"
// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initialising structs which ensures zeroing out unspecified members.
// Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
// forward declarations, we don't want to include these fully
class OpenXRVulkanExtension;
class OpenXRAPI {
private:
// our singleton
static OpenXRAPI *singleton;
// layers
uint32_t num_layer_properties = 0;
XrApiLayerProperties *layer_properties = nullptr;
// extensions
uint32_t num_supported_extensions = 0;
XrExtensionProperties *supported_extensions = nullptr;
Vector<OpenXRExtensionWrapper *> registered_extension_wrappers;
Vector<const char *> enabled_extensions;
// composition layer providers
Vector<OpenXRCompositionLayerProvider *> composition_layer_providers;
// view configuration
uint32_t num_view_configuration_types = 0;
XrViewConfigurationType *supported_view_configuration_types = nullptr;
// reference spaces
uint32_t num_reference_spaces = 0;
XrReferenceSpaceType *supported_reference_spaces = nullptr;
// swapchains (note these are platform dependent)
uint32_t num_swapchain_formats = 0;
int64_t *supported_swapchain_formats = nullptr;
// configuration
XrFormFactor form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
XrViewConfigurationType view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
// state
XrInstance instance = XR_NULL_HANDLE;
XrSystemId system_id;
String system_name;
uint32_t vendor_id;
XrSystemTrackingProperties tracking_properties;
XrSession session = XR_NULL_HANDLE;
XrSessionState session_state = XR_SESSION_STATE_UNKNOWN;
bool running = false;
XrFrameState frame_state = { XR_TYPE_FRAME_STATE, NULL, 0, 0, false };
OpenXRGraphicsExtensionWrapper *graphics_extension = nullptr;
XrSystemGraphicsProperties graphics_properties;
void *swapchain_graphics_data = nullptr;
uint32_t image_index = 0;
bool image_acquired = false;
uint32_t view_count = 0;
XrViewConfigurationView *view_configuration_views = nullptr;
XrView *views = nullptr;
XrCompositionLayerProjectionView *projection_views = nullptr;
XrSwapchain swapchain = XR_NULL_HANDLE;
XrSpace play_space = XR_NULL_HANDLE;
XrSpace view_space = XR_NULL_HANDLE;
bool view_pose_valid = false;
XRPose::TrackingConfidence head_pose_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
bool load_layer_properties();
bool load_supported_extensions();
bool is_extension_supported(const char *p_extension) const;
// instance
bool create_instance();
bool get_system_info();
bool load_supported_view_configuration_types();
bool is_view_configuration_supported(XrViewConfigurationType p_configuration_type) const;
bool load_supported_view_configuration_views(XrViewConfigurationType p_configuration_type);
void destroy_instance();
// session
bool create_session();
bool load_supported_reference_spaces();
bool is_reference_space_supported(XrReferenceSpaceType p_reference_space);
bool setup_spaces();
bool load_supported_swapchain_formats();
bool is_swapchain_format_supported(int64_t p_swapchain_format);
bool create_main_swapchain();
void destroy_session();
// swapchains
bool create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
bool acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index);
bool release_image(XrSwapchain p_swapchain);
// action map
struct Path {
XrPath path;
};
RID_Owner<Path, true> xr_path_owner;
struct ActionSet {
bool is_attached;
XrActionSet handle;
};
RID_Owner<ActionSet, true> action_set_owner;
struct PathWithSpace {
XrPath toplevel_path;
XrSpace space;
bool was_location_valid;
};
struct Action {
XrActionType action_type;
Vector<PathWithSpace> toplevel_paths;
XrAction handle;
};
RID_Owner<Action, true> action_owner;
// state changes
bool poll_events();
bool on_state_idle();
bool on_state_ready();
bool on_state_synchronized();
bool on_state_visible();
bool on_state_focused();
bool on_state_stopping();
bool on_state_loss_pending();
bool on_state_exiting();
// convencience
void copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len);
protected:
friend class OpenXRVulkanExtension;
XrInstance get_instance() const { return instance; };
XrSystemId get_system_id() const { return system_id; };
XrSession get_session() const { return session; };
// helper method to convert an XrPosef to a Transform3D
Transform3D transform_from_pose(const XrPosef &p_pose);
// helper method to get a valid Transform3D from an openxr space location
XRPose::TrackingConfidence transform_from_location(const XrSpaceLocation &p_location, Transform3D &r_transform);
XRPose::TrackingConfidence transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform);
void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity);
public:
static void setup_global_defs();
static bool openxr_is_enabled();
static OpenXRAPI *get_singleton();
String get_error_string(XrResult result);
String get_swapchain_format_name(int64_t p_swapchain_format) const;
void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
bool is_initialized();
bool is_running();
bool initialise(const String &p_rendering_driver);
bool initialise_session();
void finish();
XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; };
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; };
Size2 get_recommended_target_size();
XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix);
bool process();
void pre_render();
bool pre_draw_viewport(RID p_render_target);
void post_draw_viewport(RID p_render_target);
void end_frame();
// action map
String get_default_action_map_resource_name();
RID path_create(const String p_name);
void path_free(RID p_path);
RID action_set_create(const String p_name, const String p_localized_name, const int p_priority);
bool action_set_attach(RID p_action_set);
void action_set_free(RID p_action_set);
RID action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_toplevel_paths);
void action_free(RID p_action);
struct Binding {
RID action;
String path;
};
bool suggest_bindings(const String p_interaction_profile, const Vector<Binding> p_bindings);
bool sync_action_sets(const Vector<RID> p_active_sets);
bool get_action_bool(RID p_action, RID p_path);
float get_action_float(RID p_action, RID p_path);
Vector2 get_action_vector2(RID p_action, RID p_path);
XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_path, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool trigger_haptic_pulse(RID p_action, RID p_path, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
OpenXRAPI();
~OpenXRAPI();
};
#endif // !OPENXR_DRIVER_H

View File

@ -0,0 +1,663 @@
/*************************************************************************/
/* openxr_interface.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_interface.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "servers/rendering/rendering_server_globals.h"
void OpenXRInterface::_bind_methods() {
// todo
}
StringName OpenXRInterface::get_name() const {
return StringName("OpenXR");
};
uint32_t OpenXRInterface::get_capabilities() const {
return XRInterface::XR_VR + XRInterface::XR_STEREO;
};
XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const {
return tracking_state;
}
void OpenXRInterface::_load_action_map() {
ERR_FAIL_NULL(openxr_api);
// This may seem a bit duplicitous to a little bit of background info here.
// OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in.
// This gives the user the ability to edit the action map in a UI and customise the actions.
// OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it.
// This system does that push and we store the info needed to then work with this action map going forward.
// Within our openxr device we maintain a number of classes that wrap the relevant OpenXR objects for this.
// Within OpenXRInterface we have a few internal classes that keep track of what we've created.
// This allow us to process the relevant actions each frame.
// just in case clean up
free_action_sets();
free_trackers();
Ref<OpenXRActionMap> action_map;
if (Engine::get_singleton()->is_editor_hint()) {
#ifdef TOOLS_ENABLED
action_map.instantiate();
action_map->create_editor_action_sets();
#endif
} else {
String default_tres_name = openxr_api->get_default_action_map_resource_name();
// Check if we can load our default
if (ResourceLoader::exists(default_tres_name)) {
action_map = ResourceLoader::load(default_tres_name);
}
// Check if we need to create default action set
if (action_map.is_null()) {
action_map.instantiate();
action_map->create_default_action_sets();
#ifdef TOOLS_ENABLED
// Save our action sets so our user can
action_map->set_path(default_tres_name, true);
ResourceSaver::save(default_tres_name, action_map);
#endif
}
}
// process our action map
if (action_map.is_valid()) {
Map<Ref<OpenXRAction>, RID> action_rids;
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
// Create our action set
Ref<OpenXRActionSet> xr_action_set = action_sets[i];
ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority());
if (!action_set) {
continue;
}
// Now create our actions for these
Array actions = xr_action_set->get_actions();
for (int j = 0; j < actions.size(); j++) {
Ref<OpenXRAction> xr_action = actions[j];
PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();
Vector<RID> toplevel_rids;
Vector<Tracker *> trackers;
for (int k = 0; k < toplevel_paths.size(); k++) {
Tracker *tracker = get_tracker(toplevel_paths[k]);
if (tracker) {
toplevel_rids.push_back(tracker->path_rid);
trackers.push_back(tracker);
}
}
Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), toplevel_rids);
if (action) {
// we link our actions back to our trackers so we know which actions to check when we're processing our trackers
for (int t = 0; t < trackers.size(); t++) {
link_action_to_tracker(trackers[t], action);
}
// add this to our map for creating our interaction profiles
action_rids[xr_action] = action->action_rid;
}
}
}
// now do our suggestions
Array interaction_profiles = action_map->get_interaction_profiles();
for (int i = 0; i < interaction_profiles.size(); i++) {
Vector<OpenXRAPI::Binding> bindings;
Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profiles[i];
Array xr_bindings = xr_interaction_profile->get_bindings();
for (int j = 0; j < xr_bindings.size(); j++) {
Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];
Ref<OpenXRAction> xr_action = xr_binding->get_action();
OpenXRAPI::Binding binding;
if (action_rids.has(xr_action)) {
binding.action = action_rids[xr_action];
} else {
print_line("Action ", xr_action->get_name(), " isn't part of an action set!");
continue;
}
PackedStringArray xr_paths = xr_binding->get_paths();
for (int k = 0; k < xr_paths.size(); k++) {
binding.path = xr_paths[k];
bindings.push_back(binding);
}
}
openxr_api->suggest_bindings(xr_interaction_profile->get_interaction_profile_path(), bindings);
}
}
}
OpenXRInterface::ActionSet *OpenXRInterface::create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority) {
ERR_FAIL_NULL_V(openxr_api, nullptr);
// find if it already exists
for (int i = 0; i < action_sets.size(); i++) {
if (action_sets[i]->action_set_name == p_action_set_name) {
// already exists in this set
return nullptr;
}
}
ActionSet *action_set = memnew(ActionSet);
action_set->action_set_name = p_action_set_name;
action_set->is_active = true;
action_set->action_set_rid = openxr_api->action_set_create(p_action_set_name, p_localized_name, p_priority);
action_sets.push_back(action_set);
return action_set;
}
void OpenXRInterface::free_action_sets() {
ERR_FAIL_NULL(openxr_api);
for (int i = 0; i < action_sets.size(); i++) {
ActionSet *action_set = action_sets[i];
openxr_api->path_free(action_set->action_set_rid);
free_actions(action_set);
memfree(action_set);
}
action_sets.clear();
}
OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> p_toplevel_paths) {
ERR_FAIL_NULL_V(openxr_api, nullptr);
for (int i = 0; i < p_action_set->actions.size(); i++) {
if (p_action_set->actions[i]->action_name == p_action_name) {
// already exists in this set
return nullptr;
}
}
Action *action = memnew(Action);
action->action_name = p_action_name;
action->action_type = p_action_type;
action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, p_toplevel_paths);
p_action_set->actions.push_back(action);
return action;
}
OpenXRInterface::Action *OpenXRInterface::find_action(const String &p_action_name) {
// We just find the first action by this name
for (int i = 0; i < action_sets.size(); i++) {
for (int j = 0; j < action_sets[i]->actions.size(); j++) {
if (action_sets[i]->actions[j]->action_name == p_action_name) {
return action_sets[i]->actions[j];
}
}
}
// not found
return nullptr;
}
void OpenXRInterface::free_actions(ActionSet *p_action_set) {
ERR_FAIL_NULL(openxr_api);
for (int i = 0; i < p_action_set->actions.size(); i++) {
Action *action = p_action_set->actions[i];
openxr_api->action_free(action->action_rid);
memdelete(action);
}
p_action_set->actions.clear();
}
OpenXRInterface::Tracker *OpenXRInterface::get_tracker(const String &p_path_name) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, nullptr);
ERR_FAIL_NULL_V(openxr_api, nullptr);
Tracker *tracker = nullptr;
for (int i = 0; i < trackers.size(); i++) {
tracker = trackers[i];
if (tracker->path_name == p_path_name) {
return tracker;
}
}
// create our positional tracker
Ref<XRPositionalTracker> positional_tracker;
positional_tracker.instantiate();
// We have standardised some names to make things nicer to the user so lets recognise the toplevel paths related to these.
if (p_path_name == "/user/hand/left") {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name("left_hand");
positional_tracker->set_tracker_desc("Left hand controller");
positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);
} else if (p_path_name == "/user/hand/right") {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name("right_hand");
positional_tracker->set_tracker_desc("Right hand controller");
positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);
} else {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name(p_path_name);
positional_tracker->set_tracker_desc(p_path_name);
}
xr_server->add_tracker(positional_tracker);
// create a new entry
tracker = memnew(Tracker);
tracker->path_name = p_path_name;
tracker->path_rid = openxr_api->path_create(p_path_name);
tracker->positional_tracker = positional_tracker;
trackers.push_back(tracker);
return tracker;
}
OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_positional_tracker_name) {
for (int i = 0; i < trackers.size(); i++) {
Tracker *tracker = trackers[i];
if (tracker->positional_tracker.is_valid() && tracker->positional_tracker->get_tracker_name() == p_positional_tracker_name) {
return tracker;
}
}
return nullptr;
}
void OpenXRInterface::link_action_to_tracker(Tracker *p_tracker, Action *p_action) {
if (p_tracker->actions.find(p_action) == -1) {
p_tracker->actions.push_back(p_action);
}
}
void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
ERR_FAIL_NULL(openxr_api);
ERR_FAIL_COND(p_tracker->positional_tracker.is_null());
// handle all the actions
for (int i = 0; i < p_tracker->actions.size(); i++) {
Action *action = p_tracker->actions[i];
switch (action->action_type) {
case OpenXRAction::OPENXR_ACTION_BOOL: {
bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->path_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(pressed));
} break;
case OpenXRAction::OPENXR_ACTION_FLOAT: {
real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->path_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->path_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_POSE: {
Transform3D transform;
Vector3 linear, angular;
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->path_rid, transform, linear, angular);
if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
String name;
// We can't have dual action names in OpenXR hence we added _pose, but default, aim and grip and default pose action names in Godot so rename them on the tracker.
// NOTE need to decide on whether we should keep the naming convention or rename it on Godots side
if (action->action_name == "default_pose") {
name = "default";
} else if (action->action_name == "aim_pose") {
name = "aim";
} else if (action->action_name == "grip_pose") {
name = "grip";
} else {
name = action->action_name;
}
p_tracker->positional_tracker->set_pose(name, transform, linear, angular, confidence);
}
} break;
default: {
// not yet supported
} break;
}
}
}
void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
ERR_FAIL_NULL(openxr_api);
Action *action = find_action(p_action_name);
ERR_FAIL_NULL(action);
Tracker *tracker = find_tracker(p_tracker_name);
ERR_FAIL_NULL(tracker);
// TODO OpenXR does not support delay, so we may need to add support for that somehow...
XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds
openxr_api->trigger_haptic_pulse(action->action_rid, tracker->path_rid, p_frequency, p_amplitude, duration);
}
void OpenXRInterface::free_trackers() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
ERR_FAIL_NULL(openxr_api);
for (int i = 0; i < trackers.size(); i++) {
Tracker *tracker = trackers[i];
openxr_api->path_free(tracker->path_rid);
xr_server->remove_tracker(tracker->positional_tracker);
tracker->positional_tracker.unref();
memdelete(tracker);
}
trackers.clear();
}
bool OpenXRInterface::initialise_on_startup() const {
if (openxr_api == nullptr) {
return false;
} else if (!openxr_api->is_initialized()) {
return false;
} else {
return true;
}
}
bool OpenXRInterface::is_initialized() const {
return initialized;
};
bool OpenXRInterface::initialize() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, false);
if (openxr_api == nullptr) {
return false;
} else if (!openxr_api->is_initialized()) {
return false;
} else if (initialized) {
return true;
}
// load up our action sets before setting up our session, note that our profiles are suggestions, OpenXR takes ownership of (re)binding
_load_action_map();
if (!openxr_api->initialise_session()) {
return false;
}
// we must create a tracker for our head
head.instantiate();
head->set_tracker_type(XRServer::TRACKER_HEAD);
head->set_tracker_name("head");
head->set_tracker_desc("Players head");
xr_server->add_tracker(head);
// attach action sets
for (int i = 0; i < action_sets.size(); i++) {
openxr_api->action_set_attach(action_sets[i]->action_set_rid);
}
// make this our primary interface
xr_server->set_primary_interface(this);
initialized = true;
return initialized;
}
void OpenXRInterface::uninitialize() {
// Our OpenXR driver will clean itself up properly when Godot exits, so we just do some basic stuff here
// end the session if we need to?
// cleanup stuff
free_action_sets();
free_trackers();
XRServer *xr_server = XRServer::get_singleton();
if (xr_server) {
if (head.is_valid()) {
xr_server->remove_tracker(head);
head.unref();
}
}
initialized = false;
}
bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return false;
}
XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {
return XRInterface::XR_PLAY_AREA_UNKNOWN;
}
bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return false;
}
Size2 OpenXRInterface::get_render_target_size() {
if (openxr_api == nullptr) {
return Size2();
} else {
return openxr_api->get_recommended_target_size();
}
}
uint32_t OpenXRInterface::get_view_count() {
// TODO set this based on our configuration
return 2;
}
void OpenXRInterface::_set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye) {
p_transform = Transform3D();
// if we're not tracking, don't put our head on the floor...
p_transform.origin.y = 1.5 * p_world_scale;
// overkill but..
if (p_eye == 1) {
p_transform.origin.x = 0.03 * p_world_scale;
} else if (p_eye == 2) {
p_transform.origin.x = -0.03 * p_world_scale;
}
}
Transform3D OpenXRInterface::get_camera_transform() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, Transform3D());
Transform3D hmd_transform;
double world_scale = xr_server->get_world_scale();
// head_transform should be updated in process
hmd_transform.basis = head_transform.basis;
hmd_transform.origin = head_transform.origin * world_scale;
return hmd_transform;
}
Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, Transform3D());
Transform3D t;
if (openxr_api && openxr_api->get_view_transform(p_view, t)) {
// update our cached value if we have a valid transform
transform_for_view[p_view] = t;
} else {
// reuse cached value
t = transform_for_view[p_view];
}
// Apply our world scale
double world_scale = xr_server->get_world_scale();
t.origin *= world_scale;
return p_cam_transform * xr_server->get_reference_frame() * t;
}
CameraMatrix OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
CameraMatrix cm;
if (openxr_api) {
if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {
return cm;
}
}
// Failed to get from our OpenXR device? Default to some sort of sensible camera matrix..
cm.set_for_hmd(p_view + 1, 1.0, 6.0, 14.5, 4.0, 1.5, p_z_near, p_z_far);
return cm;
}
void OpenXRInterface::process() {
if (openxr_api) {
// do our normal process
if (openxr_api->process()) {
Transform3D t;
Vector3 linear_velocity;
Vector3 angular_velocity;
XRPose::TrackingConfidence confidence = openxr_api->get_head_center(t, linear_velocity, angular_velocity);
if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
// Only update our transform if we have one to update it with
// note that poses are stored without world scale and reference frame applied!
head_transform = t;
head_linear_velocity = linear_velocity;
head_angular_velocity = angular_velocity;
}
}
// handle our action sets....
Vector<RID> active_sets;
for (int i = 0; i < action_sets.size(); i++) {
if (action_sets[i]->is_active) {
active_sets.push_back(action_sets[i]->action_set_rid);
}
}
if (openxr_api->sync_action_sets(active_sets)) {
for (int i = 0; i < trackers.size(); i++) {
handle_tracker(trackers[i]);
}
}
}
if (head.is_valid()) {
// TODO figure out how to get our velocities
head->set_pose("default", head_transform, head_linear_velocity, head_angular_velocity);
// TODO set confidence on pose once we support tracking this..
}
}
void OpenXRInterface::pre_render() {
if (openxr_api) {
openxr_api->pre_render();
}
}
bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {
if (openxr_api) {
return openxr_api->pre_draw_viewport(p_render_target);
} else {
// don't render
return false;
}
}
Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
Vector<BlitToScreen> blit_to_screen;
// If separate HMD we should output one eye to screen
if (p_screen_rect != Rect2()) {
BlitToScreen blit;
blit.render_target = p_render_target;
blit.multi_view.use_layer = true;
blit.multi_view.layer = 0;
blit.lens_distortion.apply = false;
Size2 render_size = get_render_target_size();
Rect2 dst_rect = p_screen_rect;
float new_height = dst_rect.size.x * (render_size.y / render_size.x);
if (new_height > dst_rect.size.y) {
dst_rect.position.y = (0.5 * dst_rect.size.y) - (0.5 * new_height);
dst_rect.size.y = new_height;
} else {
float new_width = dst_rect.size.y * (render_size.x / render_size.y);
dst_rect.position.x = (0.5 * dst_rect.size.x) - (0.5 * new_width);
dst_rect.size.x = new_width;
}
blit.dst_rect = dst_rect;
blit_to_screen.push_back(blit);
}
if (openxr_api) {
openxr_api->post_draw_viewport(p_render_target);
}
return blit_to_screen;
}
void OpenXRInterface::end_frame() {
if (openxr_api) {
openxr_api->end_frame();
}
}
OpenXRInterface::OpenXRInterface() {
openxr_api = OpenXRAPI::get_singleton();
// while we don't have head tracking, don't put the headset on the floor...
_set_default_pos(head_transform, 1.0, 0);
_set_default_pos(transform_for_view[0], 1.0, 1);
_set_default_pos(transform_for_view[1], 1.0, 2);
}
OpenXRInterface::~OpenXRInterface() {
openxr_api = nullptr;
}

View File

@ -0,0 +1,129 @@
/*************************************************************************/
/* openxr_interface.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_INTERFACE_H
#define OPENXR_INTERFACE_H
#include "servers/xr/xr_interface.h"
#include "servers/xr/xr_positional_tracker.h"
#include "action_map/openxr_action_map.h"
#include "openxr_api.h"
class OpenXRInterface : public XRInterface {
GDCLASS(OpenXRInterface, XRInterface);
private:
OpenXRAPI *openxr_api = nullptr;
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
// At a minimum we need a tracker for our head
Ref<XRPositionalTracker> head;
Transform3D head_transform;
Vector3 head_linear_velocity;
Vector3 head_angular_velocity;
Transform3D transform_for_view[2]; // We currently assume 2, but could be 4 for VARJO which we do not support yet
void _load_action_map();
struct Action {
String action_name;
OpenXRAction::ActionType action_type;
RID action_rid;
};
struct ActionSet {
String action_set_name;
bool is_active;
RID action_set_rid;
Vector<Action *> actions;
};
struct Tracker {
String path_name;
RID path_rid;
Ref<XRPositionalTracker> positional_tracker;
Vector<Action *> actions;
};
Vector<ActionSet *> action_sets;
Vector<Tracker *> trackers;
ActionSet *create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority);
void free_action_sets();
Action *create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> p_toplevel_paths);
Action *find_action(const String &p_action_name);
void free_actions(ActionSet *p_action_set);
Tracker *get_tracker(const String &p_path_name);
Tracker *find_tracker(const String &p_positional_tracker_name);
void link_action_to_tracker(Tracker *p_tracker, Action *p_action);
void handle_tracker(Tracker *p_tracker);
void free_trackers();
void _set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye);
protected:
static void _bind_methods();
public:
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
virtual TrackingStatus get_tracking_status() const override;
bool initialise_on_startup() const;
virtual bool is_initialized() const override;
virtual bool initialize() override;
virtual void uninitialize() override;
virtual void trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec = 0) override;
virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
virtual XRInterface::PlayAreaMode get_play_area_mode() const override;
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
virtual Size2 get_render_target_size() override;
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual void process() override;
virtual void pre_render() override;
bool pre_draw_viewport(RID p_render_target) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void end_frame() override;
OpenXRInterface();
~OpenXRInterface();
};
#endif // !OPENXR_INTERFACE_H

View File

@ -0,0 +1,291 @@
/*************************************************************************/
/* openxr_util.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "openxr_util.h"
#define ENUM_TO_STRING_CASE(e) \
case e: { \
return String(#e); \
} break;
// TODO see if we can generate this code further using the xml file with meta data supplied by OpenXR
String OpenXRUtil::get_view_configuration_name(XrViewConfigurationType p_view_configuration) {
switch (p_view_configuration) {
ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO)
ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO)
ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO)
ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT)
ENUM_TO_STRING_CASE(XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM)
default: {
return String("View Configuration ") + String::num_int64(int64_t(p_view_configuration));
} break;
}
}
String OpenXRUtil::get_reference_space_name(XrReferenceSpaceType p_reference_space) {
switch (p_reference_space) {
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_VIEW)
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_LOCAL)
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_STAGE)
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT)
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO)
ENUM_TO_STRING_CASE(XR_REFERENCE_SPACE_TYPE_MAX_ENUM)
default: {
return String("Reference space ") + String::num_int64(int64_t(p_reference_space));
} break;
}
}
String OpenXRUtil::get_structure_type_name(XrStructureType p_structure_type) {
switch (p_structure_type) {
ENUM_TO_STRING_CASE(XR_TYPE_UNKNOWN)
ENUM_TO_STRING_CASE(XR_TYPE_API_LAYER_PROPERTIES)
ENUM_TO_STRING_CASE(XR_TYPE_EXTENSION_PROPERTIES)
ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_GET_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_PROPERTIES)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_LOCATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW)
ENUM_TO_STRING_CASE(XR_TYPE_SESSION_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SESSION_BEGIN_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_STATE)
ENUM_TO_STRING_CASE(XR_TYPE_FRAME_END_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_HAPTIC_VIBRATION)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_BUFFER)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_BOOLEAN)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_FLOAT)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_VECTOR2F)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_POSE)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_SET_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_PROPERTIES)
ENUM_TO_STRING_CASE(XR_TYPE_FRAME_WAIT_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PROJECTION)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_QUAD)
ENUM_TO_STRING_CASE(XR_TYPE_REFERENCE_SPACE_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_SPACE_CREATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_VIEW)
ENUM_TO_STRING_CASE(XR_TYPE_SPACE_LOCATION)
ENUM_TO_STRING_CASE(XR_TYPE_SPACE_VELOCITY)
ENUM_TO_STRING_CASE(XR_TYPE_FRAME_STATE)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_PROPERTIES)
ENUM_TO_STRING_CASE(XR_TYPE_FRAME_BEGIN_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_EVENTS_LOST)
ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED)
ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_STATE)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_ACTION_STATE_GET_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_HAPTIC_ACTION_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_ACTIONS_SYNC_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_CUBE_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_SWAPCHAIN_FORMAT_LIST_CREATE_INFO_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_DEBUG_UTILS_LABEL_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_XCB_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_D3D11_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_D3D12_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_EYE_GAZE_SAMPLE_TIME_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_VISIBILITY_MASK_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_MAIN_SESSION_VISIBILITY_CHANGED_EXTX)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_GRAPHICS_BINDING_EGL_MNDX)
ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINT_LOCATIONS_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINT_VELOCITIES_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_HAND_TRACKING_MESH_PROPERTIES_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_SPACE_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_UPDATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_MESH_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_POSE_TYPE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SESSION_BEGIN_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_STATE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_STATE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_FRAME_END_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SECONDARY_VIEW_CONFIGURATION_SWAPCHAIN_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_CONTROLLER_MODEL_STATE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_CONFIGURATION_VIEW_FOV_EPIC)
ENUM_TO_STRING_CASE(XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB)
ENUM_TO_STRING_CASE(XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT)
ENUM_TO_STRING_CASE(XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_NEW_SCENE_COMPUTE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_VISUAL_MESH_COMPUTE_LOD_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_GET_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENT_LOCATIONS_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENTS_LOCATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBJECTS_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_COMPONENT_PARENT_FILTER_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_OBJECT_TYPES_FILTER_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_PLANES_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_PLANE_ALIGNMENT_FILTER_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESHES_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_BUFFERS_GET_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_BUFFERS_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_VERTEX_BUFFER_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_INDICES_UINT32_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_MESH_INDICES_UINT16_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SERIALIZED_SCENE_FRAGMENT_DATA_GET_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SCENE_DESERIALIZE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB)
ENUM_TO_STRING_CASE(XR_TYPE_VIVE_TRACKER_PATHS_HTCX)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_MESH_FB)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_SCALE_FB)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_AIM_STATE_FB)
ENUM_TO_STRING_CASE(XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB)
ENUM_TO_STRING_CASE(XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB)
ENUM_TO_STRING_CASE(XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB)
ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB)
ENUM_TO_STRING_CASE(XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB)
ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_STYLE_FB)
ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB)
ENUM_TO_STRING_CASE(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB)
ENUM_TO_STRING_CASE(XR_TYPE_BINDING_MODIFICATIONS_KHR)
ENUM_TO_STRING_CASE(XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO)
ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB)
ENUM_TO_STRING_CASE(XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB)
ENUM_TO_STRING_CASE(XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB)
ENUM_TO_STRING_CASE(XR_STRUCTURE_TYPE_MAX_ENUM)
default: {
return String("Structure type ") + String::num_int64(int64_t(p_structure_type));
} break;
}
}
String OpenXRUtil::get_session_state_name(XrSessionState p_session_state) {
switch (p_session_state) {
ENUM_TO_STRING_CASE(XR_SESSION_STATE_UNKNOWN)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_IDLE)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_READY)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_SYNCHRONIZED)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_VISIBLE)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_FOCUSED)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_STOPPING)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_LOSS_PENDING)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_EXITING)
ENUM_TO_STRING_CASE(XR_SESSION_STATE_MAX_ENUM)
default: {
return String("Session state ") + String::num_int64(int64_t(p_session_state));
} break;
}
}
String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
String version;
version += String::num_int64(XR_VERSION_MAJOR(p_version));
version += String(".");
version += String::num_int64(XR_VERSION_MINOR(p_version));
version += String(".");
version += String::num_int64(XR_VERSION_PATCH(p_version));
return version;
}

View File

@ -0,0 +1,46 @@
/*************************************************************************/
/* openxr_util.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_UTIL_H
#define OPENXR_UTIL_H
#include "core/string/ustring.h"
#include <openxr/openxr.h>
class OpenXRUtil {
public:
static String get_view_configuration_name(XrViewConfigurationType p_view_configuration);
static String get_reference_space_name(XrReferenceSpaceType p_reference_space);
static String get_structure_type_name(XrStructureType p_structure_type);
static String get_session_state_name(XrSessionState p_session_state);
static String make_xr_version_string(XrVersion p_version);
};
#endif // !OPENXR_UTIL_H

View File

@ -0,0 +1,91 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "register_types.h"
#include "main/main.h"
#include "openxr_interface.h"
#include "action_map/openxr_action.h"
#include "action_map/openxr_action_map.h"
#include "action_map/openxr_action_set.h"
#include "action_map/openxr_interaction_profile.h"
OpenXRAPI *openxr_api = nullptr;
Ref<OpenXRInterface> openxr_interface;
void preregister_openxr_types() {
// For now we create our openxr device here. If we merge it with openxr_interface we'll create that here soon.
OpenXRAPI::setup_global_defs();
openxr_api = OpenXRAPI::get_singleton();
if (openxr_api) {
if (!openxr_api->initialise(Main::get_rendering_driver_name())) {
return;
}
}
}
void register_openxr_types() {
GDREGISTER_CLASS(OpenXRInterface);
GDREGISTER_CLASS(OpenXRAction);
GDREGISTER_CLASS(OpenXRActionSet);
GDREGISTER_CLASS(OpenXRActionMap);
GDREGISTER_CLASS(OpenXRIPBinding);
GDREGISTER_CLASS(OpenXRInteractionProfile);
XRServer *xr_server = XRServer::get_singleton();
if (xr_server) {
openxr_interface.instantiate();
xr_server->add_interface(openxr_interface);
if (openxr_interface->initialise_on_startup()) {
openxr_interface->initialize();
}
}
}
void unregister_openxr_types() {
if (openxr_interface.is_valid()) {
// unregister our interface from the XR server
if (XRServer::get_singleton()) {
XRServer::get_singleton()->remove_interface(openxr_interface);
}
// and release
openxr_interface.unref();
}
if (openxr_api) {
openxr_api->finish();
memdelete(openxr_api);
}
}

View File

@ -0,0 +1,40 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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. */
/*************************************************************************/
#ifndef OPENXR_REGISTER_TYPES_H
#define OPENXR_REGISTER_TYPES_H
#define MODULE_OPENXR_HAS_PREREGISTER
void preregister_openxr_types();
void register_openxr_types();
void unregister_openxr_types();
#endif // OPENXR_REGISTER_TYPES_H

View File

@ -448,11 +448,6 @@ void XRController3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand);
ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble);
ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble");
ADD_PROPERTY_DEFAULT("rumble", 0.0);
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
@ -558,20 +553,6 @@ Vector2 XRController3D::get_axis(const StringName &p_name) const {
} }
} }
real_t XRController3D::get_rumble() const {
if (!tracker.is_valid()) {
return 0.0;
}
return tracker->get_rumble();
}
void XRController3D::set_rumble(real_t p_rumble) {
if (tracker.is_valid()) {
tracker->set_rumble(p_rumble);
}
}
XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const {
// get our XRServer // get our XRServer
if (!tracker.is_valid()) { if (!tracker.is_valid()) {
@ -612,7 +593,7 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const {
} }
} }
bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
if (!xr_enabled) { if (!xr_enabled) {
warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
} }

View File

@ -139,9 +139,6 @@ public:
float get_value(const StringName &p_name) const; float get_value(const StringName &p_name) const;
Vector2 get_axis(const StringName &p_name) const; Vector2 get_axis(const StringName &p_name) const;
real_t get_rumble() const;
void set_rumble(real_t p_rumble);
XRPositionalTracker::TrackerHand get_tracker_hand() const; XRPositionalTracker::TrackerHand get_tracker_hand() const;
XRController3D() {} XRController3D() {}

View File

@ -45,7 +45,7 @@ bool RendererCompositor::is_xr_enabled() const {
} }
RendererCompositor::RendererCompositor() { RendererCompositor::RendererCompositor() {
xr_enabled = GLOBAL_GET("rendering/xr/enabled"); xr_enabled = GLOBAL_GET("xr/shaders/enabled");
} }
RendererCanvasRender *RendererCanvasRender::singleton = nullptr; RendererCanvasRender *RendererCanvasRender::singleton = nullptr;

View File

@ -332,7 +332,7 @@ void EffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer
RD::get_singleton()->draw_list_draw(draw_list, true); RD::get_singleton()->draw_list_draw(draw_list, true);
} }
void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary) { void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary, bool p_multiview) {
memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant)); memset(&copy_to_fb.push_constant, 0, sizeof(CopyToFbPushConstant));
if (p_flip_y) { if (p_flip_y) {
@ -348,10 +348,18 @@ void EffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer,
copy_to_fb.push_constant.srgb = true; copy_to_fb.push_constant.srgb = true;
} }
CopyToFBMode mode;
if (p_multiview) {
mode = p_secondary.is_valid() ? COPY_TO_FB_MULTIVIEW_WITH_DEPTH : COPY_TO_FB_MULTIVIEW;
} else {
mode = p_secondary.is_valid() ? COPY_TO_FB_COPY2 : COPY_TO_FB_COPY;
}
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_secondary.is_valid() ? COPY_TO_FB_COPY2 : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
if (p_secondary.is_valid()) { if (p_secondary.is_valid()) {
// TODO may need to do this differently when reading from depth buffer for multiview
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary), 1);
} }
RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
@ -2367,15 +2375,26 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
copy_modes.push_back("\n"); copy_modes.push_back("\n");
copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n"); copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
copy_modes.push_back("\n#define MODE_TWO_SOURCES\n"); copy_modes.push_back("\n#define MODE_TWO_SOURCES\n");
copy_modes.push_back("\n#define MULTIVIEW\n");
copy_modes.push_back("\n#define MULTIVIEW\n#define MODE_TWO_SOURCES\n");
copy_to_fb.shader.initialize(copy_modes); copy_to_fb.shader.initialize(copy_modes);
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW, false);
copy_to_fb.shader.set_variant_enabled(COPY_TO_FB_MULTIVIEW_WITH_DEPTH, false);
}
copy_to_fb.shader_version = copy_to_fb.shader.version_create(); copy_to_fb.shader_version = copy_to_fb.shader.version_create();
//use additive //use additive
for (int i = 0; i < COPY_TO_FB_MAX; i++) { for (int i = 0; i < COPY_TO_FB_MAX; i++) {
copy_to_fb.pipelines[i].setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); if (copy_to_fb.shader.is_variant_enabled(i)) {
copy_to_fb.pipelines[i].setup(copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, i), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
} else {
copy_to_fb.pipelines[i].clear();
}
} }
} }

View File

@ -203,6 +203,9 @@ private:
COPY_TO_FB_COPY, COPY_TO_FB_COPY,
COPY_TO_FB_COPY_PANORAMA_TO_DP, COPY_TO_FB_COPY_PANORAMA_TO_DP,
COPY_TO_FB_COPY2, COPY_TO_FB_COPY2,
COPY_TO_FB_MULTIVIEW,
COPY_TO_FB_MULTIVIEW_WITH_DEPTH,
COPY_TO_FB_MAX, COPY_TO_FB_MAX,
}; };
@ -893,7 +896,7 @@ public:
bool get_prefer_raster_effects(); bool get_prefer_raster_effects();
void fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RID p_destination_texture, const Size2i &p_internal_size, const Size2i &p_size, float p_fsr_upscale_sharpness); void fsr_upscale(RID p_source_rd_texture, RID p_secondary_texture, RID p_destination_texture, const Size2i &p_internal_size, const Size2i &p_size, float p_fsr_upscale_sharpness);
void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID()); void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID(), bool p_multiview = false);
void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false); void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false, bool p_alpha_to_one = false);
void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array); void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false); void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);

View File

@ -4,7 +4,20 @@
#VERSION_DEFINES #VERSION_DEFINES
#ifdef MULTIVIEW
#ifdef has_VK_KHR_multiview
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
#else // has_VK_KHR_multiview
#define ViewIndex 0
#endif // has_VK_KHR_multiview
#endif //MULTIVIEW
#ifdef MULTIVIEW
layout(location = 0) out vec3 uv_interp;
#else
layout(location = 0) out vec2 uv_interp; layout(location = 0) out vec2 uv_interp;
#endif
layout(push_constant, std430) uniform Params { layout(push_constant, std430) uniform Params {
vec4 section; vec4 section;
@ -19,9 +32,11 @@ params;
void main() { void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex]; uv_interp.xy = base_arr[gl_VertexIndex];
#ifdef MULTIVIEW
vec2 vpos = uv_interp; uv_interp.z = ViewIndex;
#endif
vec2 vpos = uv_interp.xy;
if (params.use_section) { if (params.use_section) {
vpos = params.section.xy + vpos * params.section.zw; vpos = params.section.xy + vpos * params.section.zw;
} }
@ -39,6 +54,15 @@ void main() {
#VERSION_DEFINES #VERSION_DEFINES
#ifdef MULTIVIEW
#ifdef has_VK_KHR_multiview
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
#else // has_VK_KHR_multiview
#define ViewIndex 0
#endif // has_VK_KHR_multiview
#endif //MULTIVIEW
layout(push_constant, std430) uniform Params { layout(push_constant, std430) uniform Params {
vec4 section; vec4 section;
vec2 pixel_size; vec2 pixel_size;
@ -52,12 +76,25 @@ layout(push_constant, std430) uniform Params {
} }
params; params;
#ifdef MULTIVIEW
layout(location = 0) in vec3 uv_interp;
#else
layout(location = 0) in vec2 uv_interp; layout(location = 0) in vec2 uv_interp;
#endif
#ifdef MULTIVIEW
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
#ifdef MODE_TWO_SOURCES
layout(set = 1, binding = 0) uniform sampler2DArray source_depth;
layout(location = 1) out float depth;
#endif /* MODE_TWO_SOURCES */
#else
layout(set = 0, binding = 0) uniform sampler2D source_color; layout(set = 0, binding = 0) uniform sampler2D source_color;
#ifdef MODE_TWO_SOURCES #ifdef MODE_TWO_SOURCES
layout(set = 1, binding = 0) uniform sampler2D source_color2; layout(set = 1, binding = 0) uniform sampler2D source_color2;
#endif #endif /* MODE_TWO_SOURCES */
#endif /* MULTIVIEW */
layout(location = 0) out vec4 frag_color; layout(location = 0) out vec4 frag_color;
vec3 linear_to_srgb(vec3 color) { vec3 linear_to_srgb(vec3 color) {
@ -68,9 +105,14 @@ vec3 linear_to_srgb(vec3 color) {
} }
void main() { void main() {
#ifdef MULTIVIEW
vec3 uv = uv_interp;
#else
vec2 uv = uv_interp; vec2 uv = uv_interp;
#endif
#ifdef MODE_PANORAMA_TO_DP #ifdef MODE_PANORAMA_TO_DP
// Note, multiview and panorama should not be mixed at this time
//obtain normal from dual paraboloid uv //obtain normal from dual paraboloid uv
#define M_PI 3.14159265359 #define M_PI 3.14159265359
@ -98,10 +140,20 @@ void main() {
uv = 1.0 - uv; uv = 1.0 - uv;
} }
#endif #endif
#ifdef MULTIVIEW
vec4 color = textureLod(source_color, uv, 0.0);
#ifdef MODE_TWO_SOURCES
// In multiview our 2nd input will be our depth map
depth = textureLod(source_depth, uv, 0.0).r;
#endif /* MODE_TWO_SOURCES */
#else
vec4 color = textureLod(source_color, uv, 0.0); vec4 color = textureLod(source_color, uv, 0.0);
#ifdef MODE_TWO_SOURCES #ifdef MODE_TWO_SOURCES
color += textureLod(source_color2, uv, 0.0); color += textureLod(source_color2, uv, 0.0);
#endif #endif /* MODE_TWO_SOURCES */
#endif /* MULTIVIEW */
if (params.force_luminance) { if (params.force_luminance) {
color.rgb = vec3(max(max(color.r, color.g), color.b)); color.rgb = vec3(max(max(color.r, color.g), color.b));
} }

View File

@ -548,7 +548,6 @@ void RendererViewport::draw_viewports() {
// get our xr interface in case we need it // get our xr interface in case we need it
Ref<XRInterface> xr_interface; Ref<XRInterface> xr_interface;
XRServer *xr_server = XRServer::get_singleton(); XRServer *xr_server = XRServer::get_singleton();
if (xr_server != nullptr) { if (xr_server != nullptr) {
// let our XR server know we're about to render our frames so we can get our frame timing // let our XR server know we're about to render our frames so we can get our frame timing

View File

@ -500,6 +500,7 @@ public:
virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()) = 0; virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()) = 0;
virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture) = 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 { enum TextureSliceType {
TEXTURE_SLICE_2D, TEXTURE_SLICE_2D,

View File

@ -2999,7 +2999,7 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512); GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1")); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1"));
GLOBAL_DEF_RST("rendering/xr/enabled", false); GLOBAL_DEF_RST_BASIC("xr/shaders/enabled", false);
GLOBAL_DEF_RST("rendering/2d/options/use_software_skinning", true); GLOBAL_DEF_RST("rendering/2d/options/use_software_skinning", true);
GLOBAL_DEF_RST("rendering/2d/options/ninepatch_mode", 1); GLOBAL_DEF_RST("rendering/2d/options/ninepatch_mode", 1);

View File

@ -65,10 +65,6 @@ void XRPositionalTracker::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value"))); ADD_SIGNAL(MethodInfo("input_value_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "value")));
ADD_SIGNAL(MethodInfo("input_axis_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "vector"))); ADD_SIGNAL(MethodInfo("input_axis_changed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::VECTOR2, "vector")));
ClassDB::bind_method(D_METHOD("get_rumble"), &XRPositionalTracker::get_rumble);
ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRPositionalTracker::set_rumble);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble"), "set_rumble", "get_rumble");
}; };
void XRPositionalTracker::set_tracker_type(XRServer::TrackerType p_type) { void XRPositionalTracker::set_tracker_type(XRServer::TrackerType p_type) {
@ -206,21 +202,8 @@ void XRPositionalTracker::set_input(const StringName &p_action_name, const Varia
} }
} }
real_t XRPositionalTracker::get_rumble() const {
return rumble;
};
void XRPositionalTracker::set_rumble(real_t p_rumble) {
if (p_rumble > 0.0) {
rumble = p_rumble;
} else {
rumble = 0.0;
};
};
XRPositionalTracker::XRPositionalTracker() { XRPositionalTracker::XRPositionalTracker() {
type = XRServer::TRACKER_UNKNOWN; type = XRServer::TRACKER_UNKNOWN;
name = "Unknown"; name = "Unknown";
hand = TRACKER_HAND_UNKNOWN; hand = TRACKER_HAND_UNKNOWN;
rumble = 0.0;
}; };

View File

@ -62,10 +62,6 @@ private:
Map<StringName, Ref<XRPose>> poses; Map<StringName, Ref<XRPose>> poses;
Map<StringName, Variant> inputs; Map<StringName, Variant> inputs;
int joy_id; // if we also have a related joystick entity, the id of the joystick
Ref<Mesh> mesh; // when available, a mesh that can be used to render this tracker
real_t rumble; // rumble strength, 0.0 is off, 1.0 is maximum, note that we only record here, xr_interface is responsible for execution
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -87,10 +83,6 @@ public:
Variant get_input(const StringName &p_action_name) const; Variant get_input(const StringName &p_action_name) const;
void set_input(const StringName &p_action_name, const Variant &p_value); void set_input(const StringName &p_action_name, const Variant &p_value);
// TODO replace by new implementation
real_t get_rumble() const;
void set_rumble(real_t p_rumble);
XRPositionalTracker(); XRPositionalTracker();
~XRPositionalTracker() {} ~XRPositionalTracker() {}
}; };

View File

@ -348,9 +348,10 @@ PackedStringArray XRServer::get_suggested_pose_names(const StringName &p_tracker
} }
void XRServer::_process() { void XRServer::_process() {
/* called from renderer_viewport.draw_viewports right before we start drawing our viewports */ // called from our main game loop before we handle physics and game logic
// note that we can have multiple interfaces active if we have interfaces that purely handle tracking
/* process all active interfaces */ // process all active interfaces
for (int i = 0; i < interfaces.size(); i++) { for (int i = 0; i < interfaces.size(); i++) {
if (!interfaces[i].is_valid()) { if (!interfaces[i].is_valid()) {
// ignore, not a valid reference // ignore, not a valid reference

25
thirdparty/README.md vendored
View File

@ -474,6 +474,7 @@ Files extracted from the upstream source:
- Files in `core/` folder. - Files in `core/` folder.
- `LICENSE.txt` and `CHANGELOG.md` - `LICENSE.txt` and `CHANGELOG.md`
## oidn ## oidn
- Upstream: https://github.com/OpenImageDenoise/oidn - Upstream: https://github.com/OpenImageDenoise/oidn
@ -505,6 +506,30 @@ Patch files are provided in `oidn/patches/`.
- scripts/resource_to_cpp.py (used in modules/denoise/resource_to_cpp.py) - scripts/resource_to_cpp.py (used in modules/denoise/resource_to_cpp.py)
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
- Version: 1.0.22 (458984d7f59d1ae6dc1b597d94b02e4f7132eaba, 2022)
- License: Apache 2.0
Files extracted from upstream source:
- include/
- src/common/
- src/loader/
- src/*.{c,h}
- src/external/jsoncpp/include/
- src/external/jsoncpp/src/lib_json/
- LICENSE and COPYING.adoc
Exclude:
- src/external/android-jni-wrappers and src/external/jnipp (not used yet)
- All CMake stuff: cmake/, CMakeLists.txt and *.cmake
- All Gradle stuff: *gradle*, AndroidManifest.xml
- All following files (and their .license files): *.{def,in,json,map,pom,rc}
## pcre2 ## pcre2
- Upstream: http://www.pcre.org - Upstream: http://www.pcre.org

123
thirdparty/openxr/COPYING.adoc vendored Normal file
View File

@ -0,0 +1,123 @@
= COPYING.adoc for the Khronos Group OpenXR projects
// Copyright (c) 2020-2022, The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0
This document is shared across a number of OpenXR GitHub projects, as the
set of files in those projects is partially overlapping.
(There is a single "source of truth" internal Khronos GitLab repo these
GitHub repositories interact with.)
== Licenses
The OpenXR GitHub projects use several licenses.
In general, we work to maintain compliance with the
https://reuse.software/spec/[REUSE 3.0 specification] with clear copyright
holders and license identifier listed for each file, preferably in each
file.
Where this is not possible, or e.g. when we are using files unmodified from
other open-source projects, license data is listed:
* in an adjacent file of the same name, with the additional extension
"`.license`"
* in the repository-wide "`.reuse/dep5`" copyright description
https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/["DEP5"
machine-readable copyright data] file.
The https://github.com/fsfe/reuse-tool["`reuse`" command line tool] can be
used to create a software bill of materials in SPDX format from this data.
Note that this tool will typically exclude the generated files, so if the
BOM is important to you, you may consider using the
https://github.com/KhronosGroup/OpenXR-SDK[OpenXR-SDK] repository that
contains the API headers and the loader source with all generated files
pre-generated.
The data in/adjacent to each file is the authoritative license and copyright
data.
However, for ease of understanding, the following general practices can be
observed.
(If in doubt, or if the following summary conflicts with the per-file data,
the per-file data remains authoritative.)
* The source files (in asciidoctor and other formats) for the OpenXR
Specification, reference pages, and supporting documentation are licensed
under the Creative Commons Attribution 4.0 International License (SPDX
license identifier "`CC-BY-4.0`").
* Header files, scripts, programs, XML files, and other tooling used or
generated as part of the build process is licensed under the Apache
License, Version 2.0.
* For compatibility with external developers working in GPLed projects who
have requested it, the main OpenXR headers, XML registry, and loader
source are licensed under a dual license with the SPDX license identifier
"`Apache-2.0 OR MIT`" .
Relevant files include:
** "`specification/registry/xr.xml`"
** "`include/openxr/openxr_platform_defines.h`"
** The generated OpenXR headers "`openxr.h`", "`openxr_platform.h`", and
"`openxr_reflection.h`".
** Source files in "`src/loader/`", and a few files in "`src/common/`".
** Generated source files used by the loader (including pre-generated in
OpenXR-SDK): "`common_config.h`", "`xr_generated_loader.cpp`", and
"`xr_generated_loader.hpp`".
* There are a few files adopted from other open source projects.
Such files continue under their original licenses, and appropriately
annotated in accordance with REUSE.
* Some generated, transient files produced during the course of building the
specification, headers, or other targets may not have copyrights.
These are typically very short asciidoc fragments describing parts of the
OpenXR API, and are incorporated by reference into specification or
reference page builds.
Users outside Khronos who create and post OpenXR Specifications, whether
modified or not, should use the CC-BY-4.0 license on the output documents
(HTML, PDF, etc.) they generate.
== Frequently Asked Questions
Q: Why are the HTML and PDF Specifications posted on Khronos' website under
a license which is neither CC-BY-4.0 nor Apache 2.0?
A: The Specifications posted by Khronos in the OpenXR Registry are licensed
under the proprietary Khronos Specification License.
Only these Specifications are Ratified by the Khronos Board of Promoters,
and therefore they are the only Specifications covered by the Khronos
Intellectual Property Rights Policy.
Q: Does Khronos allow the creation and distribution of modified versions of
the OpenXR Specification, such as translations to other languages?
A: Yes.
Such modified Specifications, since they are not created by Khronos, should
be placed under the CC-BY-4.0 license.
If you believe your modifications are of general interest, consider
contributing them back by making a pull request (PR) on the OpenXR-Docs
project.
Q: Can I contribute changes to the OpenXR Specification?
A: Yes, by opening an Issue or Pull Request (PR) on the
link:https://github.com/KhronosGroup/OpenXR-Docs/[OpenXR-Docs] GitHub
project.
You must execute a click-through Contributor License Agreement, which brings
your changes under the umbrella of the Khronos IP policy.
Q: Can you change the license on your files so they're compatible with my
license?
A: We are using a dual license license on `xr.xml`, the main API headers,
and the loader source files, to make them compatible with GPL-2.0- and
LGPL-2.0/2.1-licensed projects.
This replaces earlier approaches of an MIT-like license on the XML and
Apache 2.0 on all headers, and allows use of the SPDX license identifier
"`Apache-2.0 OR MIT`" to denote the license.
If you *require* this same compatibility for use of other Apache-2.0
licensed files in our repository, please raise an issue identifying the
files and we will consider changing those specific files to the dual license
as well.

202
thirdparty/openxr/LICENSE vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

3925
thirdparty/openxr/include/openxr/openxr.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,675 @@
#ifndef OPENXR_PLATFORM_H_
#define OPENXR_PLATFORM_H_ 1
/*
** Copyright (c) 2017-2022, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
** This header is generated from the Khronos OpenXR XML API Registry.
**
*/
#include "openxr.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_KHR_android_thread_settings 1
#define XR_KHR_android_thread_settings_SPEC_VERSION 5
#define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings"
typedef enum XrAndroidThreadTypeKHR {
XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR = 1,
XR_ANDROID_THREAD_TYPE_APPLICATION_WORKER_KHR = 2,
XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR = 3,
XR_ANDROID_THREAD_TYPE_RENDERER_WORKER_KHR = 4,
XR_ANDROID_THREAD_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
} XrAndroidThreadTypeKHR;
typedef XrResult (XRAPI_PTR *PFN_xrSetAndroidApplicationThreadKHR)(XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR(
XrSession session,
XrAndroidThreadTypeKHR threadType,
uint32_t threadId);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_KHR_android_surface_swapchain 1
#define XR_KHR_android_surface_swapchain_SPEC_VERSION 4
#define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain"
typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR(
XrSession session,
const XrSwapchainCreateInfo* info,
XrSwapchain* swapchain,
jobject* surface);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_KHR_android_create_instance 1
#define XR_KHR_android_create_instance_SPEC_VERSION 3
#define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance"
// XrInstanceCreateInfoAndroidKHR extends XrInstanceCreateInfo
typedef struct XrInstanceCreateInfoAndroidKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
void* XR_MAY_ALIAS applicationVM;
void* XR_MAY_ALIAS applicationActivity;
} XrInstanceCreateInfoAndroidKHR;
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_GRAPHICS_API_VULKAN
#define XR_KHR_vulkan_swapchain_format_list 1
#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 4
#define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list"
typedef struct XrVulkanSwapchainFormatListCreateInfoKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
uint32_t viewFormatCount;
const VkFormat* viewFormats;
} XrVulkanSwapchainFormatListCreateInfoKHR;
#endif /* XR_USE_GRAPHICS_API_VULKAN */
#ifdef XR_USE_GRAPHICS_API_OPENGL
#define XR_KHR_opengl_enable 1
#define XR_KHR_opengl_enable_SPEC_VERSION 10
#define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable"
#ifdef XR_USE_PLATFORM_WIN32
// XrGraphicsBindingOpenGLWin32KHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingOpenGLWin32KHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
HDC hDC;
HGLRC hGLRC;
} XrGraphicsBindingOpenGLWin32KHR;
#endif // XR_USE_PLATFORM_WIN32
#ifdef XR_USE_PLATFORM_XLIB
// XrGraphicsBindingOpenGLXlibKHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingOpenGLXlibKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
Display* xDisplay;
uint32_t visualid;
GLXFBConfig glxFBConfig;
GLXDrawable glxDrawable;
GLXContext glxContext;
} XrGraphicsBindingOpenGLXlibKHR;
#endif // XR_USE_PLATFORM_XLIB
#ifdef XR_USE_PLATFORM_XCB
// XrGraphicsBindingOpenGLXcbKHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingOpenGLXcbKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
xcb_connection_t* connection;
uint32_t screenNumber;
xcb_glx_fbconfig_t fbconfigid;
xcb_visualid_t visualid;
xcb_glx_drawable_t glxDrawable;
xcb_glx_context_t glxContext;
} XrGraphicsBindingOpenGLXcbKHR;
#endif // XR_USE_PLATFORM_XCB
#ifdef XR_USE_PLATFORM_WAYLAND
// XrGraphicsBindingOpenGLWaylandKHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingOpenGLWaylandKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
struct wl_display* display;
} XrGraphicsBindingOpenGLWaylandKHR;
#endif // XR_USE_PLATFORM_WAYLAND
typedef struct XrSwapchainImageOpenGLKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
uint32_t image;
} XrSwapchainImageOpenGLKHR;
typedef struct XrGraphicsRequirementsOpenGLKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
XrVersion minApiVersionSupported;
XrVersion maxApiVersionSupported;
} XrGraphicsRequirementsOpenGLKHR;
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_OPENGL */
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
#define XR_KHR_opengl_es_enable 1
#define XR_KHR_opengl_es_enable_SPEC_VERSION 8
#define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable"
#ifdef XR_USE_PLATFORM_ANDROID
// XrGraphicsBindingOpenGLESAndroidKHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingOpenGLESAndroidKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
EGLDisplay display;
EGLConfig config;
EGLContext context;
} XrGraphicsBindingOpenGLESAndroidKHR;
#endif // XR_USE_PLATFORM_ANDROID
typedef struct XrSwapchainImageOpenGLESKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
uint32_t image;
} XrSwapchainImageOpenGLESKHR;
typedef struct XrGraphicsRequirementsOpenGLESKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
XrVersion minApiVersionSupported;
XrVersion maxApiVersionSupported;
} XrGraphicsRequirementsOpenGLESKHR;
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
#ifdef XR_USE_GRAPHICS_API_VULKAN
#define XR_KHR_vulkan_enable 1
#define XR_KHR_vulkan_enable_SPEC_VERSION 8
#define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable"
// XrGraphicsBindingVulkanKHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingVulkanKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
VkInstance instance;
VkPhysicalDevice physicalDevice;
VkDevice device;
uint32_t queueFamilyIndex;
uint32_t queueIndex;
} XrGraphicsBindingVulkanKHR;
typedef struct XrSwapchainImageVulkanKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
VkImage image;
} XrSwapchainImageVulkanKHR;
typedef struct XrGraphicsRequirementsVulkanKHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
XrVersion minApiVersionSupported;
XrVersion maxApiVersionSupported;
} XrGraphicsRequirementsVulkanKHR;
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanInstanceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanDeviceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDeviceKHR)(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanInstanceExtensionsKHR(
XrInstance instance,
XrSystemId systemId,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer);
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanDeviceExtensionsKHR(
XrInstance instance,
XrSystemId systemId,
uint32_t bufferCapacityInput,
uint32_t* bufferCountOutput,
char* buffer);
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDeviceKHR(
XrInstance instance,
XrSystemId systemId,
VkInstance vkInstance,
VkPhysicalDevice* vkPhysicalDevice);
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_VULKAN */
#ifdef XR_USE_GRAPHICS_API_D3D11
#define XR_KHR_D3D11_enable 1
#define XR_KHR_D3D11_enable_SPEC_VERSION 8
#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
// XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingD3D11KHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
ID3D11Device* device;
} XrGraphicsBindingD3D11KHR;
typedef struct XrSwapchainImageD3D11KHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
ID3D11Texture2D* texture;
} XrSwapchainImageD3D11KHR;
typedef struct XrGraphicsRequirementsD3D11KHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
LUID adapterLuid;
D3D_FEATURE_LEVEL minFeatureLevel;
} XrGraphicsRequirementsD3D11KHR;
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_D3D11 */
#ifdef XR_USE_GRAPHICS_API_D3D12
#define XR_KHR_D3D12_enable 1
#define XR_KHR_D3D12_enable_SPEC_VERSION 8
#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
// XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingD3D12KHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
ID3D12Device* device;
ID3D12CommandQueue* queue;
} XrGraphicsBindingD3D12KHR;
typedef struct XrSwapchainImageD3D12KHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
ID3D12Resource* texture;
} XrSwapchainImageD3D12KHR;
typedef struct XrGraphicsRequirementsD3D12KHR {
XrStructureType type;
void* XR_MAY_ALIAS next;
LUID adapterLuid;
D3D_FEATURE_LEVEL minFeatureLevel;
} XrGraphicsRequirementsD3D12KHR;
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_D3D12 */
#ifdef XR_USE_PLATFORM_WIN32
#define XR_KHR_win32_convert_performance_counter_time 1
#define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1
#define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time"
typedef XrResult (XRAPI_PTR *PFN_xrConvertWin32PerformanceCounterToTimeKHR)(XrInstance instance, const LARGE_INTEGER* performanceCounter, XrTime* time);
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToWin32PerformanceCounterKHR)(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrConvertWin32PerformanceCounterToTimeKHR(
XrInstance instance,
const LARGE_INTEGER* performanceCounter,
XrTime* time);
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR(
XrInstance instance,
XrTime time,
LARGE_INTEGER* performanceCounter);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_PLATFORM_WIN32 */
#ifdef XR_USE_TIMESPEC
#define XR_KHR_convert_timespec_time 1
#define XR_KHR_convert_timespec_time_SPEC_VERSION 1
#define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time"
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimespecTimeToTimeKHR)(XrInstance instance, const struct timespec* timespecTime, XrTime* time);
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToTimespecTimeKHR)(XrInstance instance, XrTime time, struct timespec* timespecTime);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimespecTimeToTimeKHR(
XrInstance instance,
const struct timespec* timespecTime,
XrTime* time);
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR(
XrInstance instance,
XrTime time,
struct timespec* timespecTime);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_TIMESPEC */
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_KHR_loader_init_android 1
#define XR_KHR_loader_init_android_SPEC_VERSION 1
#define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android"
typedef struct XrLoaderInitInfoAndroidKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
void* XR_MAY_ALIAS applicationVM;
void* XR_MAY_ALIAS applicationContext;
} XrLoaderInitInfoAndroidKHR;
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_GRAPHICS_API_VULKAN
#define XR_KHR_vulkan_enable2 1
#define XR_KHR_vulkan_enable2_SPEC_VERSION 2
#define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2"
typedef XrFlags64 XrVulkanInstanceCreateFlagsKHR;
// Flag bits for XrVulkanInstanceCreateFlagsKHR
typedef XrFlags64 XrVulkanDeviceCreateFlagsKHR;
// Flag bits for XrVulkanDeviceCreateFlagsKHR
typedef struct XrVulkanInstanceCreateInfoKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrSystemId systemId;
XrVulkanInstanceCreateFlagsKHR createFlags;
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
const VkInstanceCreateInfo* vulkanCreateInfo;
const VkAllocationCallbacks* vulkanAllocator;
} XrVulkanInstanceCreateInfoKHR;
typedef struct XrVulkanDeviceCreateInfoKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrSystemId systemId;
XrVulkanDeviceCreateFlagsKHR createFlags;
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
VkPhysicalDevice vulkanPhysicalDevice;
const VkDeviceCreateInfo* vulkanCreateInfo;
const VkAllocationCallbacks* vulkanAllocator;
} XrVulkanDeviceCreateInfoKHR;
typedef XrGraphicsBindingVulkanKHR XrGraphicsBindingVulkan2KHR;
typedef struct XrVulkanGraphicsDeviceGetInfoKHR {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrSystemId systemId;
VkInstance vulkanInstance;
} XrVulkanGraphicsDeviceGetInfoKHR;
typedef XrSwapchainImageVulkanKHR XrSwapchainImageVulkan2KHR;
typedef XrGraphicsRequirementsVulkanKHR XrGraphicsRequirementsVulkan2KHR;
typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult);
typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirements2KHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanInstanceKHR(
XrInstance instance,
const XrVulkanInstanceCreateInfoKHR* createInfo,
VkInstance* vulkanInstance,
VkResult* vulkanResult);
XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanDeviceKHR(
XrInstance instance,
const XrVulkanDeviceCreateInfoKHR* createInfo,
VkDevice* vulkanDevice,
VkResult* vulkanResult);
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDevice2KHR(
XrInstance instance,
const XrVulkanGraphicsDeviceGetInfoKHR* getInfo,
VkPhysicalDevice* vulkanPhysicalDevice);
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR(
XrInstance instance,
XrSystemId systemId,
XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_VULKAN */
#ifdef XR_USE_PLATFORM_EGL
#define XR_MNDX_egl_enable 1
#define XR_MNDX_egl_enable_SPEC_VERSION 1
#define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable"
// XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo
typedef struct XrGraphicsBindingEGLMNDX {
XrStructureType type;
const void* XR_MAY_ALIAS next;
PFNEGLGETPROCADDRESSPROC getProcAddress;
EGLDisplay display;
EGLConfig config;
EGLContext context;
} XrGraphicsBindingEGLMNDX;
#endif /* XR_USE_PLATFORM_EGL */
#ifdef XR_USE_PLATFORM_WIN32
#define XR_MSFT_perception_anchor_interop 1
#define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1
#define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop"
typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT)(XrSession session, IUnknown* perceptionAnchor, XrSpatialAnchorMSFT* anchor);
typedef XrResult (XRAPI_PTR *PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT)(XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPerceptionAnchorMSFT(
XrSession session,
IUnknown* perceptionAnchor,
XrSpatialAnchorMSFT* anchor);
XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(
XrSession session,
XrSpatialAnchorMSFT anchor,
IUnknown** perceptionAnchor);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_PLATFORM_WIN32 */
#ifdef XR_USE_PLATFORM_WIN32
#define XR_MSFT_holographic_window_attachment 1
#define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1
#define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment"
#ifdef XR_USE_PLATFORM_WIN32
// XrHolographicWindowAttachmentMSFT extends XrSessionCreateInfo
typedef struct XrHolographicWindowAttachmentMSFT {
XrStructureType type;
const void* XR_MAY_ALIAS next;
IUnknown* holographicSpace;
IUnknown* coreWindow;
} XrHolographicWindowAttachmentMSFT;
#endif // XR_USE_PLATFORM_WIN32
#endif /* XR_USE_PLATFORM_WIN32 */
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_FB_android_surface_swapchain_create 1
#define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1
#define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create"
typedef XrFlags64 XrAndroidSurfaceSwapchainFlagsFB;
// Flag bits for XrAndroidSurfaceSwapchainFlagsFB
static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_SYNCHRONOUS_BIT_FB = 0x00000001;
static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_USE_TIMESTAMPS_BIT_FB = 0x00000002;
#ifdef XR_USE_PLATFORM_ANDROID
// XrAndroidSurfaceSwapchainCreateInfoFB extends XrSwapchainCreateInfo
typedef struct XrAndroidSurfaceSwapchainCreateInfoFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrAndroidSurfaceSwapchainFlagsFB createFlags;
} XrAndroidSurfaceSwapchainCreateInfoFB;
#endif // XR_USE_PLATFORM_ANDROID
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_PLATFORM_WIN32
#define XR_OCULUS_audio_device_guid 1
#define XR_OCULUS_audio_device_guid_SPEC_VERSION 1
#define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid"
#define XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS 128
typedef XrResult (XRAPI_PTR *PFN_xrGetAudioOutputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
typedef XrResult (XRAPI_PTR *PFN_xrGetAudioInputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioOutputDeviceGuidOculus(
XrInstance instance,
wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus(
XrInstance instance,
wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_PLATFORM_WIN32 */
#ifdef XR_USE_GRAPHICS_API_VULKAN
#define XR_FB_foveation_vulkan 1
#define XR_FB_foveation_vulkan_SPEC_VERSION 1
#define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan"
// XrSwapchainImageFoveationVulkanFB extends XrSwapchainImageVulkanKHR
typedef struct XrSwapchainImageFoveationVulkanFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
VkImage image;
uint32_t width;
uint32_t height;
} XrSwapchainImageFoveationVulkanFB;
#endif /* XR_USE_GRAPHICS_API_VULKAN */
#ifdef XR_USE_PLATFORM_ANDROID
#define XR_FB_swapchain_update_state_android_surface 1
#define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface"
#ifdef XR_USE_PLATFORM_ANDROID
typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
uint32_t width;
uint32_t height;
} XrSwapchainStateAndroidSurfaceDimensionsFB;
#endif // XR_USE_PLATFORM_ANDROID
#endif /* XR_USE_PLATFORM_ANDROID */
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
#define XR_FB_swapchain_update_state_opengl_es 1
#define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es"
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
typedef struct XrSwapchainStateSamplerOpenGLESFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
EGLenum minFilter;
EGLenum magFilter;
EGLenum wrapModeS;
EGLenum wrapModeT;
EGLenum swizzleRed;
EGLenum swizzleGreen;
EGLenum swizzleBlue;
EGLenum swizzleAlpha;
float maxAnisotropy;
XrColor4f borderColor;
} XrSwapchainStateSamplerOpenGLESFB;
#endif // XR_USE_GRAPHICS_API_OPENGL_ES
#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
#ifdef XR_USE_GRAPHICS_API_VULKAN
#define XR_FB_swapchain_update_state_vulkan 1
#define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan"
#ifdef XR_USE_GRAPHICS_API_VULKAN
typedef struct XrSwapchainStateSamplerVulkanFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
VkFilter minFilter;
VkFilter magFilter;
VkSamplerMipmapMode mipmapMode;
VkSamplerAddressMode wrapModeS;
VkSamplerAddressMode wrapModeT;
VkComponentSwizzle swizzleRed;
VkComponentSwizzle swizzleGreen;
VkComponentSwizzle swizzleBlue;
VkComponentSwizzle swizzleAlpha;
float maxAnisotropy;
XrColor4f borderColor;
} XrSwapchainStateSamplerVulkanFB;
#endif // XR_USE_GRAPHICS_API_VULKAN
#endif /* XR_USE_GRAPHICS_API_VULKAN */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,110 @@
/*
** Copyright (c) 2017-2022, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#ifndef OPENXR_PLATFORM_DEFINES_H_
#define OPENXR_PLATFORM_DEFINES_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/* Platform-specific calling convention macros.
*
* Platforms should define these so that OpenXR clients call OpenXR functions
* with the same calling conventions that the OpenXR implementation expects.
*
* XRAPI_ATTR - Placed before the return type in function declarations.
* Useful for C++11 and GCC/Clang-style function attribute syntax.
* XRAPI_CALL - Placed after the return type in function declarations.
* Useful for MSVC-style calling convention syntax.
* XRAPI_PTR - Placed between the '(' and '*' in function pointer types.
*
* Function declaration: XRAPI_ATTR void XRAPI_CALL xrFunction(void);
* Function pointer type: typedef void (XRAPI_PTR *PFN_xrFunction)(void);
*/
#if defined(_WIN32)
#define XRAPI_ATTR
// On Windows, functions use the stdcall convention
#define XRAPI_CALL __stdcall
#define XRAPI_PTR XRAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
#error "API not supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
// On Android 32-bit ARM targets, functions use the "hardfloat"
// calling convention, i.e. float parameters are passed in registers. This
// is true even if the rest of the application passes floats on the stack,
// as it does by default when compiling for the armeabi-v7a NDK ABI.
#define XRAPI_ATTR __attribute__((pcs("aapcs-vfp")))
#define XRAPI_CALL
#define XRAPI_PTR XRAPI_ATTR
#else
// On other platforms, use the default calling convention
#define XRAPI_ATTR
#define XRAPI_CALL
#define XRAPI_PTR
#endif
#include <stddef.h>
#if !defined(XR_NO_STDINT_H)
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#endif // !defined( XR_NO_STDINT_H )
// XR_PTR_SIZE (in bytes)
#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
#define XR_PTR_SIZE 8
#else
#define XR_PTR_SIZE 4
#endif
// Needed so we can use clang __has_feature portably.
#if !defined(XR_COMPILER_HAS_FEATURE)
#if defined(__clang__)
#define XR_COMPILER_HAS_FEATURE(x) __has_feature(x)
#else
#define XR_COMPILER_HAS_FEATURE(x) 0
#endif
#endif
// Identifies if the current compiler has C++11 support enabled.
// Does not by itself identify if any given C++11 feature is present.
#if !defined(XR_CPP11_ENABLED) && defined(__cplusplus)
#if defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
#define XR_CPP11_ENABLED 1
#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
#define XR_CPP11_ENABLED 1
#elif (__cplusplus >= 201103L) // 201103 is the first C++11 version.
#define XR_CPP11_ENABLED 1
#endif
#endif
// Identifies if the current compiler supports C++11 nullptr.
#if !defined(XR_CPP_NULLPTR_SUPPORTED)
#if defined(XR_CPP11_ENABLED) && \
((defined(__clang__) && XR_COMPILER_HAS_FEATURE(cxx_nullptr)) || \
(defined(__GNUC__) && (((__GNUC__ * 1000) + __GNUC_MINOR__) >= 4006)) || \
(defined(_MSC_VER) && (_MSC_VER >= 1600)) || \
(defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403)))
#define XR_CPP_NULLPTR_SUPPORTED 1
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

10
thirdparty/openxr/src/.clang-format vendored Normal file
View File

@ -0,0 +1,10 @@
---
# Copyright (c) 2017-2022, The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0
# Use defaults from the Google style with the following exceptions:
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 132
SortIncludes: false
...

View File

@ -0,0 +1,47 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
//
/*!
* @file
*
* Additional functions along the lines of the standard library algorithms.
*/
#pragma once
#include <algorithm>
#include <vector>
/// Like std::remove_if, except it works on associative containers and it actually removes this.
///
/// The iterator stuff in here is subtle - .erase() invalidates only that iterator, but it returns a non-invalidated iterator to the
/// next valid element which we can use instead of incrementing.
template <typename T, typename Pred>
static inline void map_erase_if(T &container, Pred &&predicate) {
for (auto it = container.begin(); it != container.end();) {
if (predicate(*it)) {
it = container.erase(it);
} else {
++it;
}
}
}
/*!
* Moves all elements matching the predicate to the end of the vector then erases them.
*
* Combines the two parts of the erase-remove idiom to simplify things and avoid accidentally using the wrong erase overload.
*/
template <typename T, typename Alloc, typename Pred>
static inline void vector_remove_if_and_erase(std::vector<T, Alloc> &vec, Pred &&predicate) {
auto b = vec.begin();
auto e = vec.end();
vec.erase(std::remove_if(b, e, std::forward<Pred>(predicate)), e);
}

View File

@ -0,0 +1,322 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Authors: Mark Young <marky@lunarg.com>
// Nat Brown <natb@valvesoftware.com>
//
#include "filesystem_utils.hpp"
#include "platform_utils.hpp"
#include <cstring>
#include <string>
#if defined DISABLE_STD_FILESYSTEM
#define USE_EXPERIMENTAL_FS 0
#define USE_FINAL_FS 0
#else
#include "stdfs_conditions.h"
#endif
#if USE_FINAL_FS == 1
#include <filesystem>
#define FS_PREFIX std::filesystem
#elif USE_EXPERIMENTAL_FS == 1
#include <experimental/filesystem>
#define FS_PREFIX std::experimental::filesystem
#elif defined(XR_USE_PLATFORM_WIN32)
// Windows fallback includes
#include <stdint.h>
#include <direct.h>
#else
// Linux/Apple fallback includes
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <dirent.h>
#endif
#if defined(XR_USE_PLATFORM_WIN32)
#define PATH_SEPARATOR ';'
#define DIRECTORY_SYMBOL '\\'
#define ALTERNATE_DIRECTORY_SYMBOL '/'
#else
#define PATH_SEPARATOR ':'
#define DIRECTORY_SYMBOL '/'
#endif
#if (USE_FINAL_FS == 1) || (USE_EXPERIMENTAL_FS == 1)
// We can use one of the C++ filesystem packages
bool FileSysUtilsIsRegularFile(const std::string& path) { return FS_PREFIX::is_regular_file(path); }
bool FileSysUtilsIsDirectory(const std::string& path) { return FS_PREFIX::is_directory(path); }
bool FileSysUtilsPathExists(const std::string& path) { return FS_PREFIX::exists(path); }
bool FileSysUtilsIsAbsolutePath(const std::string& path) {
FS_PREFIX::path file_path(path);
return file_path.is_absolute();
}
bool FileSysUtilsGetCurrentPath(std::string& path) {
FS_PREFIX::path cur_path = FS_PREFIX::current_path();
path = cur_path.string();
return true;
}
bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) {
FS_PREFIX::path path_var(file_path);
parent_path = path_var.parent_path().string();
return true;
}
bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) {
absolute = FS_PREFIX::absolute(path).string();
return true;
}
bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical) {
#if defined(XR_USE_PLATFORM_WIN32)
// std::filesystem::canonical fails on UWP and must be avoided. Further, PathCchCanonicalize is not available on Windows 7 and
// PathCanonicalizeW is not available on UWP. However, symbolic links are not important on Windows since the loader uses the
// registry for indirection instead, and so this function can be a no-op on Windows.
canonical = path;
#else
canonical = FS_PREFIX::canonical(path).string();
#endif
return true;
}
bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) {
FS_PREFIX::path parent_path(parent);
FS_PREFIX::path child_path(child);
FS_PREFIX::path full_path = parent_path / child_path;
combined = full_path.string();
return true;
}
bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) {
std::string::size_type start = 0;
std::string::size_type location = path_list.find(PATH_SEPARATOR);
while (location != std::string::npos) {
paths.push_back(path_list.substr(start, location));
start = location + 1;
location = path_list.find(PATH_SEPARATOR, start);
}
paths.push_back(path_list.substr(start, location));
return true;
}
bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) {
for (auto& dir_iter : FS_PREFIX::directory_iterator(path)) {
files.push_back(dir_iter.path().filename().string());
}
return true;
}
#elif defined(XR_OS_WINDOWS)
// For pre C++17 compiler that doesn't support experimental filesystem
bool FileSysUtilsIsRegularFile(const std::string& path) {
const DWORD attr = GetFileAttributesW(utf8_to_wide(path).c_str());
return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
}
bool FileSysUtilsIsDirectory(const std::string& path) {
const DWORD attr = GetFileAttributesW(utf8_to_wide(path).c_str());
return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
}
bool FileSysUtilsPathExists(const std::string& path) {
return (GetFileAttributesW(utf8_to_wide(path).c_str()) != INVALID_FILE_ATTRIBUTES);
}
bool FileSysUtilsIsAbsolutePath(const std::string& path) {
bool pathStartsWithDir = (path.size() >= 1) && ((path[0] == DIRECTORY_SYMBOL) || (path[0] == ALTERNATE_DIRECTORY_SYMBOL));
bool pathStartsWithDrive =
(path.size() >= 3) && (path[1] == ':' && (path[2] == DIRECTORY_SYMBOL || path[2] == ALTERNATE_DIRECTORY_SYMBOL));
return pathStartsWithDir || pathStartsWithDrive;
}
bool FileSysUtilsGetCurrentPath(std::string& path) {
wchar_t tmp_path[MAX_PATH];
if (nullptr != _wgetcwd(tmp_path, MAX_PATH - 1)) {
path = wide_to_utf8(tmp_path);
return true;
}
return false;
}
bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) {
std::string full_path;
if (FileSysUtilsGetAbsolutePath(file_path, full_path)) {
std::string::size_type lastSeparator = full_path.find_last_of(DIRECTORY_SYMBOL);
parent_path = (lastSeparator == 0) ? full_path : full_path.substr(0, lastSeparator);
return true;
}
return false;
}
bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) {
wchar_t tmp_path[MAX_PATH];
if (0 != GetFullPathNameW(utf8_to_wide(path).c_str(), MAX_PATH, tmp_path, NULL)) {
absolute = wide_to_utf8(tmp_path);
return true;
}
return false;
}
bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& absolute) {
// PathCchCanonicalize is not available on Windows 7 and PathCanonicalizeW is not available on UWP. However, symbolic links are
// not important on Windows since the loader uses the registry for indirection instead, and so this function can be a no-op on
// Windows.
absolute = path;
return true;
}
bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) {
std::string::size_type parent_len = parent.length();
if (0 == parent_len || "." == parent || ".\\" == parent || "./" == parent) {
combined = child;
return true;
}
char last_char = parent[parent_len - 1];
if ((last_char == DIRECTORY_SYMBOL) || (last_char == ALTERNATE_DIRECTORY_SYMBOL)) {
parent_len--;
}
combined = parent.substr(0, parent_len) + DIRECTORY_SYMBOL + child;
return true;
}
bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) {
std::string::size_type start = 0;
std::string::size_type location = path_list.find(PATH_SEPARATOR);
while (location != std::string::npos) {
paths.push_back(path_list.substr(start, location));
start = location + 1;
location = path_list.find(PATH_SEPARATOR, start);
}
paths.push_back(path_list.substr(start, location));
return true;
}
bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) {
std::string searchPath;
FileSysUtilsCombinePaths(path, "*", searchPath);
WIN32_FIND_DATAW file_data;
HANDLE file_handle = FindFirstFileW(utf8_to_wide(searchPath).c_str(), &file_data);
if (file_handle != INVALID_HANDLE_VALUE) {
do {
if (!(file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
files.push_back(wide_to_utf8(file_data.cFileName));
}
} while (FindNextFileW(file_handle, &file_data));
return true;
}
return false;
}
#else // XR_OS_LINUX/XR_OS_APPLE fallback
// simple POSIX-compatible implementation of the <filesystem> pieces used by OpenXR
bool FileSysUtilsIsRegularFile(const std::string& path) {
struct stat path_stat;
stat(path.c_str(), &path_stat);
return S_ISREG(path_stat.st_mode);
}
bool FileSysUtilsIsDirectory(const std::string& path) {
struct stat path_stat;
stat(path.c_str(), &path_stat);
return S_ISDIR(path_stat.st_mode);
}
bool FileSysUtilsPathExists(const std::string& path) { return (access(path.c_str(), F_OK) != -1); }
bool FileSysUtilsIsAbsolutePath(const std::string& path) { return (path[0] == DIRECTORY_SYMBOL); }
bool FileSysUtilsGetCurrentPath(std::string& path) {
char tmp_path[PATH_MAX];
if (nullptr != getcwd(tmp_path, PATH_MAX - 1)) {
path = tmp_path;
return true;
}
return false;
}
bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path) {
std::string full_path;
if (FileSysUtilsGetAbsolutePath(file_path, full_path)) {
std::string::size_type lastSeparator = full_path.find_last_of(DIRECTORY_SYMBOL);
parent_path = (lastSeparator == 0) ? full_path : full_path.substr(0, lastSeparator);
return true;
}
return false;
}
bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute) {
// canonical path is absolute
return FileSysUtilsGetCanonicalPath(path, absolute);
}
bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical) {
char buf[PATH_MAX];
if (nullptr != realpath(path.c_str(), buf)) {
canonical = buf;
return true;
}
return false;
}
bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined) {
std::string::size_type parent_len = parent.length();
if (0 == parent_len || "." == parent || "./" == parent) {
combined = child;
return true;
}
char last_char = parent[parent_len - 1];
if (last_char == DIRECTORY_SYMBOL) {
parent_len--;
}
combined = parent.substr(0, parent_len) + DIRECTORY_SYMBOL + child;
return true;
}
bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths) {
std::string::size_type start = 0;
std::string::size_type location = path_list.find(PATH_SEPARATOR);
while (location != std::string::npos) {
paths.push_back(path_list.substr(start, location));
start = location + 1;
location = path_list.find(PATH_SEPARATOR, start);
}
paths.push_back(path_list.substr(start, location));
return true;
}
bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files) {
DIR* dir = opendir(path.c_str());
if (dir == nullptr) {
return false;
}
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
files.emplace_back(entry->d_name);
}
closedir(dir);
return true;
}
#endif

View File

@ -0,0 +1,46 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include <string>
#include <vector>
// Determine if the path indicates a regular file (not a directory or symbolic link)
bool FileSysUtilsIsRegularFile(const std::string& path);
// Determine if the path indicates a directory
bool FileSysUtilsIsDirectory(const std::string& path);
// Determine if the provided path exists on the filesystem
bool FileSysUtilsPathExists(const std::string& path);
// Get the current directory
bool FileSysUtilsGetCurrentPath(std::string& path);
// Get the parent path of a file
bool FileSysUtilsGetParentPath(const std::string& file_path, std::string& parent_path);
// Determine if the provided path is an absolute path
bool FileSysUtilsIsAbsolutePath(const std::string& path);
// Get the absolute path for a provided file
bool FileSysUtilsGetAbsolutePath(const std::string& path, std::string& absolute);
// Get the absolute path for a provided file
bool FileSysUtilsGetCanonicalPath(const std::string& path, std::string& canonical);
// Combine a parent and child directory
bool FileSysUtilsCombinePaths(const std::string& parent, const std::string& child, std::string& combined);
// Parse out individual paths in a path list
bool FileSysUtilsParsePathList(std::string& path_list, std::vector<std::string>& paths);
// Record all the filenames for files found in the provided path.
bool FileSysUtilsFindFilesInPath(const std::string& path, std::vector<std::string>& files);

View File

@ -0,0 +1,108 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
//
/*!
* @file
*
* Some utilities, primarily for working with OpenXR handles in a generic way.
*/
#pragma once
#include <openxr/openxr.h>
#include <string>
#include <stdint.h>
inline std::string to_hex(const uint8_t* const data, size_t bytes) {
std::string out(2 + bytes * 2, '?');
out[0] = '0';
out[1] = 'x';
static const char* hex = "0123456789abcdef";
auto ch = out.end();
for (size_t i = 0; i < bytes; ++i) {
auto b = data[i];
*--ch = hex[(b >> 0) & 0xf];
*--ch = hex[(b >> 4) & 0xf];
}
return out;
}
template <typename T>
inline std::string to_hex(const T& data) {
return to_hex(reinterpret_cast<const uint8_t* const>(&data), sizeof(data));
}
#if XR_PTR_SIZE == 8
/// Convert a handle into a same-sized integer.
template <typename T>
static inline uint64_t MakeHandleGeneric(T handle) {
return reinterpret_cast<uint64_t>(handle);
}
/// Treat an integer as a handle
template <typename T>
static inline T& TreatIntegerAsHandle(uint64_t& handle) {
return reinterpret_cast<T&>(handle);
}
/// @overload
template <typename T>
static inline T const& TreatIntegerAsHandle(uint64_t const& handle) {
return reinterpret_cast<T const&>(handle);
}
/// Does a correctly-sized integer represent a null handle?
static inline bool IsIntegerNullHandle(uint64_t handle) { return XR_NULL_HANDLE == reinterpret_cast<void*>(handle); }
#else
/// Convert a handle into a same-sized integer: no-op on 32-bit systems
static inline uint64_t MakeHandleGeneric(uint64_t handle) { return handle; }
/// Treat an integer as a handle: no-op on 32-bit systems
template <typename T>
static inline T& TreatIntegerAsHandle(uint64_t& handle) {
return handle;
}
/// @overload
template <typename T>
static inline T const& TreatIntegerAsHandle(uint64_t const& handle) {
return handle;
}
/// Does a correctly-sized integer represent a null handle?
static inline bool IsIntegerNullHandle(uint64_t handle) { return XR_NULL_HANDLE == handle; }
#endif
/// Turns a uint64_t into a string formatted as hex.
///
/// The core of the HandleToHexString implementation is in here.
inline std::string Uint64ToHexString(uint64_t val) { return to_hex(val); }
/// Turns a uint32_t into a string formatted as hex.
inline std::string Uint32ToHexString(uint32_t val) { return to_hex(val); }
/// Turns an OpenXR handle into a string formatted as hex.
template <typename T>
inline std::string HandleToHexString(T handle) {
return to_hex(handle);
}
/// Turns a pointer-sized integer into a string formatted as hex.
inline std::string UintptrToHexString(uintptr_t val) { return to_hex(val); }
/// Convert a pointer to a string formatted as hex.
template <typename T>
inline std::string PointerToHexString(T const* ptr) {
return to_hex(ptr);
}

View File

@ -0,0 +1,114 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include <openxr/openxr.h>
#ifdef __cplusplus
extern "C" {
#endif
// Forward declare.
typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo;
// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance.
// This function allows us to pass special API layer information to each layer during the process of creating an Instance.
typedef XrResult(XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo *info,
const XrApiLayerCreateInfo *apiLayerInfo, XrInstance *instance);
// Loader/API Layer Interface versions
// 1 - First version, introduces negotiation structure and functions
#define XR_CURRENT_LOADER_API_LAYER_VERSION 1
// Loader/Runtime Interface versions
// 1 - First version, introduces negotiation structure and functions
#define XR_CURRENT_LOADER_RUNTIME_VERSION 1
// Version negotiation values
typedef enum XrLoaderInterfaceStructs {
XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0,
XR_LOADER_INTERFACE_STRUCT_LOADER_INFO,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST,
XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO,
XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO,
} XrLoaderInterfaceStructs;
#define XR_LOADER_INFO_STRUCT_VERSION 1
typedef struct XrNegotiateLoaderInfo {
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_LOADER_INFO
uint32_t structVersion; // XR_LOADER_INFO_STRUCT_VERSION
size_t structSize; // sizeof(XrNegotiateLoaderInfo)
uint32_t minInterfaceVersion;
uint32_t maxInterfaceVersion;
XrVersion minApiVersion;
XrVersion maxApiVersion;
} XrNegotiateLoaderInfo;
#define XR_API_LAYER_INFO_STRUCT_VERSION 1
typedef struct XrNegotiateApiLayerRequest {
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST
uint32_t structVersion; // XR_API_LAYER_INFO_STRUCT_VERSION
size_t structSize; // sizeof(XrNegotiateApiLayerRequest)
uint32_t layerInterfaceVersion; // CURRENT_LOADER_API_LAYER_VERSION
XrVersion layerApiVersion;
PFN_xrGetInstanceProcAddr getInstanceProcAddr;
PFN_xrCreateApiLayerInstance createApiLayerInstance;
} XrNegotiateApiLayerRequest;
#define XR_RUNTIME_INFO_STRUCT_VERSION 1
typedef struct XrNegotiateRuntimeRequest {
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST
uint32_t structVersion; // XR_RUNTIME_INFO_STRUCT_VERSION
size_t structSize; // sizeof(XrNegotiateRuntimeRequest)
uint32_t runtimeInterfaceVersion; // CURRENT_LOADER_RUNTIME_VERSION
XrVersion runtimeApiVersion;
PFN_xrGetInstanceProcAddr getInstanceProcAddr;
} XrNegotiateRuntimeRequest;
// Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or
// more API layers needs to expose at least this function.
typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo *loaderInfo,
const char *apiLayerName,
XrNegotiateApiLayerRequest *apiLayerRequest);
// Function used to negotiate an interface betewen the loader and a runtime. Each runtime should expose
// at least this function.
typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo *loaderInfo,
XrNegotiateRuntimeRequest *runtimeRequest);
// Forward declare.
typedef struct XrApiLayerNextInfo XrApiLayerNextInfo;
#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1
struct XrApiLayerNextInfo {
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO
uint32_t structVersion; // XR_API_LAYER_NEXT_INFO_STRUCT_VERSION
size_t structSize; // sizeof(XrApiLayerNextInfo)
char layerName[XR_MAX_API_LAYER_NAME_SIZE]; // Name of API layer which should receive this info
PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; // Pointer to next API layer's xrGetInstanceProcAddr
PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; // Pointer to next API layer's xrCreateApiLayerInstance
XrApiLayerNextInfo *next; // Pointer to the next API layer info in the sequence
};
#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512
#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1
typedef struct XrApiLayerCreateInfo {
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO
uint32_t structVersion; // XR_API_LAYER_CREATE_INFO_STRUCT_VERSION
size_t structSize; // sizeof(XrApiLayerCreateInfo)
void *loaderInstance; // Pointer to the LoaderInstance class
char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; // Location to the found settings file (or empty '\0')
XrApiLayerNextInfo *nextInfo; // Pointer to the next API layer's Info
} XrApiLayerCreateInfo;
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -0,0 +1,276 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Authors: Mark Young <marky@lunarg.com>
// Ryan Pavlik <ryan.pavlik@collabora.com>
// Dave Houlton <daveh@lunarg.com>
//
#include "object_info.h"
#include "extra_algorithms.h"
#include "hex_and_handles.h"
#include <openxr/openxr.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "memory.h"
std::string XrSdkLogObjectInfo::ToString() const {
std::ostringstream oss;
oss << Uint64ToHexString(handle);
if (!name.empty()) {
oss << " (" << name << ")";
}
return oss.str();
}
void ObjectInfoCollection::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
// If name is empty, we should erase it
if (object_name.empty()) {
RemoveObject(object_handle, object_type);
return;
}
// Otherwise, add it or update the name
XrSdkLogObjectInfo new_obj = {object_handle, object_type};
// If it already exists, update the name
auto lookup_info = LookUpStoredObjectInfo(new_obj);
if (lookup_info != nullptr) {
lookup_info->name = object_name;
return;
}
// It doesn't exist, so add a new info block
new_obj.name = object_name;
object_info_.push_back(new_obj);
}
void ObjectInfoCollection::RemoveObject(uint64_t object_handle, XrObjectType object_type) {
vector_remove_if_and_erase(
object_info_, [=](XrSdkLogObjectInfo const& info) { return info.handle == object_handle && info.type == object_type; });
}
XrSdkLogObjectInfo const* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) const {
auto e = object_info_.end();
auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); });
if (it != e) {
return &(*it);
}
return nullptr;
}
XrSdkLogObjectInfo* ObjectInfoCollection::LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) {
auto e = object_info_.end();
auto it = std::find_if(object_info_.begin(), e, [&](XrSdkLogObjectInfo const& stored) { return Equivalent(stored, info); });
if (it != e) {
return &(*it);
}
return nullptr;
}
bool ObjectInfoCollection::LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const {
auto info_lookup = LookUpStoredObjectInfo(info.objectHandle, info.objectType);
if (info_lookup != nullptr) {
info.objectName = info_lookup->name.c_str();
return true;
}
return false;
}
bool ObjectInfoCollection::LookUpObjectName(XrSdkLogObjectInfo& info) const {
auto info_lookup = LookUpStoredObjectInfo(info);
if (info_lookup != nullptr) {
info.name = info_lookup->name;
return true;
}
return false;
}
static std::vector<XrDebugUtilsObjectNameInfoEXT> PopulateObjectNameInfo(std::vector<XrSdkLogObjectInfo> const& obj) {
std::vector<XrDebugUtilsObjectNameInfoEXT> ret;
ret.reserve(obj.size());
std::transform(obj.begin(), obj.end(), std::back_inserter(ret), [](XrSdkLogObjectInfo const& info) {
return XrDebugUtilsObjectNameInfoEXT{XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, info.type, info.handle,
info.name.c_str()};
});
return ret;
}
NamesAndLabels::NamesAndLabels(std::vector<XrSdkLogObjectInfo> obj, std::vector<XrDebugUtilsLabelEXT> lab)
: sdk_objects(std::move(obj)), objects(PopulateObjectNameInfo(sdk_objects)), labels(std::move(lab)) {}
void NamesAndLabels::PopulateCallbackData(XrDebugUtilsMessengerCallbackDataEXT& callback_data) const {
callback_data.objects = objects.empty() ? nullptr : const_cast<XrDebugUtilsObjectNameInfoEXT*>(objects.data());
callback_data.objectCount = static_cast<uint32_t>(objects.size());
callback_data.sessionLabels = labels.empty() ? nullptr : const_cast<XrDebugUtilsLabelEXT*>(labels.data());
callback_data.sessionLabelCount = static_cast<uint32_t>(labels.size());
}
void DebugUtilsData::LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const {
auto session_label_iterator = session_labels_.find(session);
if (session_label_iterator != session_labels_.end()) {
auto& XrSdkSessionLabels = *session_label_iterator->second;
// Copy the debug utils labels in reverse order in the the labels vector.
std::transform(XrSdkSessionLabels.rbegin(), XrSdkSessionLabels.rend(), std::back_inserter(labels),
[](XrSdkSessionLabelPtr const& label) { return label->debug_utils_label; });
}
}
XrSdkSessionLabel::XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, bool individual)
: label_name(label_info.labelName), debug_utils_label(label_info), is_individual_label(individual) {
// Update the c string pointer to the one we hold.
debug_utils_label.labelName = label_name.c_str();
}
XrSdkSessionLabelPtr XrSdkSessionLabel::make(const XrDebugUtilsLabelEXT& label_info, bool individual) {
XrSdkSessionLabelPtr ret(new XrSdkSessionLabel(label_info, individual));
return ret;
}
void DebugUtilsData::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
object_info_.AddObjectName(object_handle, object_type, object_name);
}
// We always want to remove the old individual label before we do anything else.
// So, do that in it's own method
void DebugUtilsData::RemoveIndividualLabel(XrSdkSessionLabelList& label_vec) {
if (!label_vec.empty() && label_vec.back()->is_individual_label) {
label_vec.pop_back();
}
}
XrSdkSessionLabelList* DebugUtilsData::GetSessionLabelList(XrSession session) {
auto session_label_iterator = session_labels_.find(session);
if (session_label_iterator == session_labels_.end()) {
return nullptr;
}
return session_label_iterator->second.get();
}
XrSdkSessionLabelList& DebugUtilsData::GetOrCreateSessionLabelList(XrSession session) {
XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
if (vec_ptr == nullptr) {
std::unique_ptr<XrSdkSessionLabelList> vec(new XrSdkSessionLabelList);
vec_ptr = vec.get();
session_labels_[session] = std::move(vec);
}
return *vec_ptr;
}
void DebugUtilsData::BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT& label_info) {
auto& vec = GetOrCreateSessionLabelList(session);
// Individual labels do not stay around in the transition into a new label region
RemoveIndividualLabel(vec);
// Start the new label region
vec.emplace_back(XrSdkSessionLabel::make(label_info, false));
}
void DebugUtilsData::EndLabelRegion(XrSession session) {
XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
if (vec_ptr == nullptr) {
return;
}
// Individual labels do not stay around in the transition out of label region
RemoveIndividualLabel(*vec_ptr);
// Remove the last label region
if (!vec_ptr->empty()) {
vec_ptr->pop_back();
}
}
void DebugUtilsData::InsertLabel(XrSession session, const XrDebugUtilsLabelEXT& label_info) {
auto& vec = GetOrCreateSessionLabelList(session);
// Remove any individual layer that might already be there
RemoveIndividualLabel(vec);
// Insert a new individual label
vec.emplace_back(XrSdkSessionLabel::make(label_info, true));
}
void DebugUtilsData::DeleteObject(uint64_t object_handle, XrObjectType object_type) {
object_info_.RemoveObject(object_handle, object_type);
if (object_type == XR_OBJECT_TYPE_SESSION) {
auto session = TreatIntegerAsHandle<XrSession>(object_handle);
XrSdkSessionLabelList* vec_ptr = GetSessionLabelList(session);
if (vec_ptr != nullptr) {
session_labels_.erase(session);
}
}
}
void DebugUtilsData::DeleteSessionLabels(XrSession session) { session_labels_.erase(session); }
NamesAndLabels DebugUtilsData::PopulateNamesAndLabels(std::vector<XrSdkLogObjectInfo> objects) const {
std::vector<XrDebugUtilsLabelEXT> labels;
for (auto& obj : objects) {
// Check for any names that have been associated with the objects and set them up here
object_info_.LookUpObjectName(obj);
// If this is a session, see if there are any labels associated with it for us to add
// to the callback content.
if (XR_OBJECT_TYPE_SESSION == obj.type) {
LookUpSessionLabels(obj.GetTypedHandle<XrSession>(), labels);
}
}
return {objects, labels};
}
void DebugUtilsData::WrapCallbackData(AugmentedCallbackData* aug_data,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data) const {
// If there's nothing to add, just return the original data as the augmented copy
aug_data->exported_data = callback_data;
if (object_info_.Empty() || callback_data->objectCount == 0) {
return;
}
// Inspect each of the callback objects
bool name_found = false;
for (uint32_t obj = 0; obj < callback_data->objectCount; ++obj) {
auto& current_obj = callback_data->objects[obj];
name_found |= (nullptr != object_info_.LookUpStoredObjectInfo(current_obj.objectHandle, current_obj.objectType));
// If this is a session, record any labels associated with it
if (XR_OBJECT_TYPE_SESSION == current_obj.objectType) {
XrSession session = TreatIntegerAsHandle<XrSession>(current_obj.objectHandle);
LookUpSessionLabels(session, aug_data->labels);
}
}
// If we found nothing to add, return the original data
if (!name_found && aug_data->labels.empty()) {
return;
}
// Found additional data - modify an internal copy and return that as the exported data
memcpy(&aug_data->modified_data, callback_data, sizeof(XrDebugUtilsMessengerCallbackDataEXT));
aug_data->new_objects.assign(callback_data->objects, callback_data->objects + callback_data->objectCount);
// Record (overwrite) the names of all incoming objects provided in our internal list
for (auto& obj : aug_data->new_objects) {
object_info_.LookUpObjectName(obj);
}
// Update local copy & point export to it
aug_data->modified_data.objects = aug_data->new_objects.data();
aug_data->modified_data.sessionLabelCount = static_cast<uint32_t>(aug_data->labels.size());
aug_data->modified_data.sessionLabels = aug_data->labels.empty() ? nullptr : aug_data->labels.data();
aug_data->exported_data = &aug_data->modified_data;
return;
}

View File

@ -0,0 +1,229 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Authors: Mark Young <marky@lunarg.com>, Ryan Pavlik <ryan.pavlik@collabora.com
//
/*!
* @file
*
* The core of an XR_EXT_debug_utils implementation, used/shared by the loader and several SDK layers.
*/
#pragma once
#include "hex_and_handles.h"
#include <openxr/openxr.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
struct XrSdkGenericObject {
//! Type-erased handle value
uint64_t handle;
//! Kind of object this handle refers to
XrObjectType type;
/// Un-erase the type of the handle and get it properly typed again.
///
/// Note: Does not check the type before doing it!
template <typename HandleType>
HandleType& GetTypedHandle() {
return TreatIntegerAsHandle<HandleType&>(handle);
}
//! @overload
template <typename HandleType>
HandleType const& GetTypedHandle() const {
return TreatIntegerAsHandle<HandleType&>(handle);
}
//! Create from a typed handle and object type
template <typename T>
XrSdkGenericObject(T h, XrObjectType t) : handle(MakeHandleGeneric(h)), type(t) {}
//! Create from an untyped handle value (integer) and object type
XrSdkGenericObject(uint64_t h, XrObjectType t) : handle(h), type(t) {}
};
struct XrSdkLogObjectInfo {
//! Type-erased handle value
uint64_t handle;
//! Kind of object this handle refers to
XrObjectType type;
//! To be assigned by the application - not part of this object's identity
std::string name;
/// Un-erase the type of the handle and get it properly typed again.
///
/// Note: Does not check the type before doing it!
template <typename HandleType>
HandleType& GetTypedHandle() {
return TreatIntegerAsHandle<HandleType&>(handle);
}
//! @overload
template <typename HandleType>
HandleType const& GetTypedHandle() const {
return TreatIntegerAsHandle<HandleType&>(handle);
}
XrSdkLogObjectInfo() = default;
//! Create from a typed handle and object type
template <typename T>
XrSdkLogObjectInfo(T h, XrObjectType t) : handle(MakeHandleGeneric(h)), type(t) {}
//! Create from an untyped handle value (integer) and object type
XrSdkLogObjectInfo(uint64_t h, XrObjectType t) : handle(h), type(t) {}
//! Create from an untyped handle value (integer), object type, and name
XrSdkLogObjectInfo(uint64_t h, XrObjectType t, const char* n) : handle(h), type(t), name(n == nullptr ? "" : n) {}
std::string ToString() const;
};
//! True if the two object infos have the same handle value and handle type
static inline bool Equivalent(XrSdkLogObjectInfo const& a, XrSdkLogObjectInfo const& b) {
return a.handle == b.handle && a.type == b.type;
}
//! @overload
static inline bool Equivalent(XrDebugUtilsObjectNameInfoEXT const& a, XrSdkLogObjectInfo const& b) {
return a.objectHandle == b.handle && a.objectType == b.type;
}
//! @overload
static inline bool Equivalent(XrSdkLogObjectInfo const& a, XrDebugUtilsObjectNameInfoEXT const& b) { return Equivalent(b, a); }
/// Object info registered with calls to xrSetDebugUtilsObjectNameEXT
class ObjectInfoCollection {
public:
void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);
void RemoveObject(uint64_t object_handle, XrObjectType object_type);
//! Find the stored object info, if any, matching handle and type.
//! Return nullptr if not found.
XrSdkLogObjectInfo const* LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info) const;
//! Find the stored object info, if any, matching handle and type.
//! Return nullptr if not found.
XrSdkLogObjectInfo* LookUpStoredObjectInfo(XrSdkLogObjectInfo const& info);
//! Find the stored object info, if any.
//! Return nullptr if not found.
XrSdkLogObjectInfo const* LookUpStoredObjectInfo(uint64_t handle, XrObjectType type) const {
return LookUpStoredObjectInfo({handle, type});
}
//! Find the object name, if any, and update debug utils info accordingly.
//! Return true if found and updated.
bool LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const;
//! Find the object name, if any, and update logging info accordingly.
//! Return true if found and updated.
bool LookUpObjectName(XrSdkLogObjectInfo& info) const;
//! Is the collection empty?
bool Empty() const { return object_info_.empty(); }
private:
// Object names that have been set for given objects
std::vector<XrSdkLogObjectInfo> object_info_;
};
struct XrSdkSessionLabel;
using XrSdkSessionLabelPtr = std::unique_ptr<XrSdkSessionLabel>;
using XrSdkSessionLabelList = std::vector<XrSdkSessionLabelPtr>;
struct XrSdkSessionLabel {
static XrSdkSessionLabelPtr make(const XrDebugUtilsLabelEXT& label_info, bool individual);
std::string label_name;
XrDebugUtilsLabelEXT debug_utils_label;
bool is_individual_label;
private:
XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, bool individual);
};
/// The metadata for a collection of objects. Must persist unmodified during the entire debug messenger call!
struct NamesAndLabels {
NamesAndLabels() = default;
NamesAndLabels(std::vector<XrSdkLogObjectInfo> obj, std::vector<XrDebugUtilsLabelEXT> lab);
/// C++ structure owning the data (strings) backing the objects vector.
std::vector<XrSdkLogObjectInfo> sdk_objects;
std::vector<XrDebugUtilsObjectNameInfoEXT> objects;
std::vector<XrDebugUtilsLabelEXT> labels;
/// Populate the debug utils callback data structure.
void PopulateCallbackData(XrDebugUtilsMessengerCallbackDataEXT& data) const;
// XrDebugUtilsMessengerCallbackDataEXT MakeCallbackData() const;
};
struct AugmentedCallbackData {
std::vector<XrDebugUtilsLabelEXT> labels;
std::vector<XrDebugUtilsObjectNameInfoEXT> new_objects;
XrDebugUtilsMessengerCallbackDataEXT modified_data;
const XrDebugUtilsMessengerCallbackDataEXT* exported_data;
};
/// Tracks all the data (handle names and session labels) required to fully augment XR_EXT_debug_utils-related calls.
class DebugUtilsData {
public:
DebugUtilsData() = default;
DebugUtilsData(const DebugUtilsData&) = delete;
DebugUtilsData& operator=(const DebugUtilsData&) = delete;
bool Empty() const { return object_info_.Empty() && session_labels_.empty(); }
//! Core of implementation for xrSetDebugUtilsObjectNameEXT
void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);
/// Core of implementation for xrSessionBeginDebugUtilsLabelRegionEXT
void BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT& label_info);
/// Core of implementation for xrSessionEndDebugUtilsLabelRegionEXT
void EndLabelRegion(XrSession session);
/// Core of implementation for xrSessionInsertDebugUtilsLabelEXT
void InsertLabel(XrSession session, const XrDebugUtilsLabelEXT& label_info);
/// Removes all labels associated with a session - call in xrDestroySession and xrDestroyInstance (for all child sessions)
void DeleteSessionLabels(XrSession session);
/// Retrieve labels for the given session, if any, and push them in reverse order on the vector.
void LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const;
/// Removes all data related to this object - including session labels if it's a session.
///
/// Does not take care of handling child objects - you must do this yourself.
void DeleteObject(uint64_t object_handle, XrObjectType object_type);
/// Given the collection of objects, populate their names and list of labels
NamesAndLabels PopulateNamesAndLabels(std::vector<XrSdkLogObjectInfo> objects) const;
void WrapCallbackData(AugmentedCallbackData* aug_data,
const XrDebugUtilsMessengerCallbackDataEXT* provided_callback_data) const;
private:
void RemoveIndividualLabel(XrSdkSessionLabelList& label_vec);
XrSdkSessionLabelList* GetSessionLabelList(XrSession session);
XrSdkSessionLabelList& GetOrCreateSessionLabelList(XrSession session);
// Session labels: one vector of them per session.
std::unordered_map<XrSession, std::unique_ptr<XrSdkSessionLabelList>> session_labels_;
// Names for objects.
ObjectInfoCollection object_info_;
};

View File

@ -0,0 +1,345 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com>
//
#pragma once
#include "xr_dependencies.h"
#include <string>
#include <stdlib.h>
// OpenXR paths and registry key locations
#define OPENXR_RELATIVE_PATH "openxr/"
#define OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/implicit.d"
#define OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/explicit.d"
#ifdef XR_OS_WINDOWS
#define OPENXR_REGISTRY_LOCATION "SOFTWARE\\Khronos\\OpenXR\\"
#define OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Implicit"
#define OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Explicit"
#endif
// OpenXR Loader environment variables of interest
#define OPENXR_RUNTIME_JSON_ENV_VAR "XR_RUNTIME_JSON"
#define OPENXR_API_LAYER_PATH_ENV_VAR "XR_API_LAYER_PATH"
// This is a CMake generated file with #defines for any functions/includes
// that it found present and build-time configuration.
// If you don't have this file, on non-Windows you'll need to define
// one of HAVE_SECURE_GETENV or HAVE___SECURE_GETENV depending on which
// of secure_getenv or __secure_getenv are present
#ifdef OPENXR_HAVE_COMMON_CONFIG
#include "common_config.h"
#endif // OPENXR_HAVE_COMMON_CONFIG
// Environment variables
#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
namespace detail {
static inline char* ImplGetEnv(const char* name) { return getenv(name); }
static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); }
static inline char* ImplGetSecureEnv(const char* name) {
#ifdef HAVE_SECURE_GETENV
return secure_getenv(name);
#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(name);
#else
#pragma message( \
"Warning: Falling back to non-secure getenv for environmental" \
"lookups! Consider updating to a different libc.")
return ImplGetEnv(name);
#endif
}
} // namespace detail
#endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
#if defined(XR_OS_LINUX)
static inline std::string PlatformUtilsGetEnv(const char* name) {
auto str = detail::ImplGetEnv(name);
if (str == nullptr) {
return {};
}
return str;
}
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
auto str = detail::ImplGetSecureEnv(name);
if (str == nullptr) {
return {};
}
return str;
}
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
const int shouldOverwrite = 1;
int result = detail::ImplSetEnv(name, value, shouldOverwrite);
return (result == 0);
}
#elif defined(XR_OS_APPLE)
static inline std::string PlatformUtilsGetEnv(const char* name) {
auto str = detail::ImplGetEnv(name);
if (str == nullptr) {
return {};
}
return str;
}
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
auto str = detail::ImplGetSecureEnv(name);
if (str == nullptr) {
return {};
}
return str;
}
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
const int shouldOverwrite = 1;
int result = detail::ImplSetEnv(name, value, shouldOverwrite);
return (result == 0);
}
// Prefix for the Apple global runtime JSON file name
static const std::string rt_dir_prefix = "/usr/local/share/openxr/";
static const std::string rt_filename = "/active_runtime.json";
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
file_name = rt_dir_prefix;
file_name += std::to_string(major_version);
file_name += rt_filename;
return true;
}
#elif defined(XR_OS_WINDOWS)
#if !defined(NDEBUG)
inline void LogError(const std::string& error) { OutputDebugStringA(error.c_str()); }
#else
#define LogError(x)
#endif
inline std::wstring utf8_to_wide(const std::string& utf8Text) {
if (utf8Text.empty()) {
return {};
}
std::wstring wideText;
const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0);
if (wideLength == 0) {
LogError("utf8_to_wide get size error: " + std::to_string(::GetLastError()));
return {};
}
// MultiByteToWideChar returns number of chars of the input buffer, regardless of null terminitor
wideText.resize(wideLength, 0);
wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17
const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength);
if (length != wideLength) {
LogError("utf8_to_wide convert string error: " + std::to_string(::GetLastError()));
return {};
}
return wideText;
}
inline std::string wide_to_utf8(const std::wstring& wideText) {
if (wideText.empty()) {
return {};
}
std::string narrowText;
int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr);
if (narrowLength == 0) {
LogError("wide_to_utf8 get size error: " + std::to_string(::GetLastError()));
return {};
}
// WideCharToMultiByte returns number of chars of the input buffer, regardless of null terminitor
narrowText.resize(narrowLength, 0);
char* narrowString = const_cast<char*>(narrowText.data()); // mutable data() only exists in c++17
const int length =
::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr);
if (length != narrowLength) {
LogError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError()));
return {};
}
return narrowText;
}
// Returns true if the current process has an integrity level > SECURITY_MANDATORY_MEDIUM_RID.
static inline bool IsHighIntegrityLevel() {
// Execute this check once and save the value as a static bool.
static bool isHighIntegrityLevel = ([] {
HANDLE processToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &processToken)) {
// Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD.
uint8_t mandatoryLabelBuffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]{};
DWORD bufferSize;
if (GetTokenInformation(processToken, TokenIntegrityLevel, mandatoryLabelBuffer, sizeof(mandatoryLabelBuffer),
&bufferSize) != 0) {
const auto mandatoryLabel = reinterpret_cast<const TOKEN_MANDATORY_LABEL*>(mandatoryLabelBuffer);
if (mandatoryLabel->Label.Sid != 0) {
const DWORD subAuthorityCount = *GetSidSubAuthorityCount(mandatoryLabel->Label.Sid);
const DWORD integrityLevel = *GetSidSubAuthority(mandatoryLabel->Label.Sid, subAuthorityCount - 1);
CloseHandle(processToken);
return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID;
}
}
CloseHandle(processToken);
}
return false;
})();
return isHighIntegrityLevel;
}
// Returns true if the given environment variable exists.
// The name is a case-sensitive UTF8 string.
static inline bool PlatformUtilsGetEnvSet(const char* name) {
const std::wstring wname = utf8_to_wide(name);
const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);
// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.
return 0 != valSize;
}
// Returns the environment variable value for the given name.
// Returns an empty string if the environment variable doesn't exist or if it exists but is empty.
// Use PlatformUtilsGetEnvSet to tell if it exists.
// The name is a case-sensitive UTF8 string.
static inline std::string PlatformUtilsGetEnv(const char* name) {
const std::wstring wname = utf8_to_wide(name);
const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0);
// GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error.
// The size includes the null-terminator, so a size of 1 is means the variable was explicitly set to empty.
if (valSize == 0 || valSize == 1) {
return {};
}
// GetEnvironmentVariable returns size including null terminator for "query size" call.
std::wstring wValue(valSize, 0);
wchar_t* wValueData = &wValue[0];
// GetEnvironmentVariable returns string length, excluding null terminator for "get value"
// call if there was enough capacity. Else it returns the required capacity (including null terminator).
const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size());
if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls...
LogError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError()));
return {};
}
wValue.resize(length); // Strip the null terminator.
return wide_to_utf8(wValue);
}
// Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel.
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
if (IsHighIntegrityLevel()) {
return {};
}
// No secure version for Windows so the above integrity check is needed.
return PlatformUtilsGetEnv(name);
}
// Sets an environment variable via UTF8 strings.
// The name is case-sensitive.
// Overwrites the variable if it already exists.
// Returns true if it could be set.
static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
const std::wstring wname = utf8_to_wide(name);
const std::wstring wvalue = utf8_to_wide(value);
BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str());
return (result != 0);
}
#elif defined(XR_OS_ANDROID)
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
// Stub func
return false;
}
static inline std::string PlatformUtilsGetEnv(const char* /* name */) {
// Stub func
return {};
}
static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
// Stub func
return {};
}
static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) {
// Stub func
return false;
}
#include <sys/stat.h>
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
// Prefix for the runtime JSON file name
static const char* rt_dir_prefixes[] = {"/oem", "/vendor", "/system"};
static const std::string rt_filename = "/active_runtime.json";
static const std::string subdir = "/etc/openxr/";
for (const auto prefix : rt_dir_prefixes) {
auto path = prefix + subdir + std::to_string(major_version) + rt_filename;
struct stat buf;
if (0 == stat(path.c_str(), &buf)) {
file_name = path;
return true;
}
}
return false;
}
#else // Not Linux, Apple, nor Windows
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
// Stub func
return false;
}
static inline std::string PlatformUtilsGetEnv(const char* /* name */) {
// Stub func
return {};
}
static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
// Stub func
return {};
}
static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) {
// Stub func
return false;
}
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) {
// Stub func
return false;
}
#endif

View File

@ -0,0 +1,45 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
#ifndef _STDFS_CONDITIONS_H
#define _STDFS_CONDITIONS_H
// If the C++ macro is set to the version containing C++17, it must support
// the final C++17 package
#if __cplusplus >= 201703L
#define USE_EXPERIMENTAL_FS 0
#define USE_FINAL_FS 1
#elif defined(_MSC_VER) && _MSC_VER >= 1900
#if defined(_HAS_CXX17) && _HAS_CXX17
// When MSC supports c++17 use <filesystem> package.
#define USE_EXPERIMENTAL_FS 0
#define USE_FINAL_FS 1
#endif // !_HAS_CXX17
// GCC supports the experimental filesystem items starting in GCC 6
#elif (__GNUC__ >= 6)
#define USE_EXPERIMENTAL_FS 1
#define USE_FINAL_FS 0
// If Clang, check for feature support
#elif defined(__clang__) && (__cpp_lib_filesystem || __cpp_lib_experimental_filesystem)
#if __cpp_lib_filesystem
#define USE_EXPERIMENTAL_FS 0
#define USE_FINAL_FS 1
#else
#define USE_EXPERIMENTAL_FS 1
#define USE_FINAL_FS 0
#endif
// If all above fails, fall back to standard C++ and OS-specific items
#else
#define USE_EXPERIMENTAL_FS 0
#define USE_FINAL_FS 0
#endif
#endif // !_STDFS_CONDITIONS_H

View File

@ -0,0 +1,89 @@
// Copyright (c) 2018-2022, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// This file includes headers with types which openxr.h depends on in order
// to compile when platforms, graphics apis, and the like are enabled.
#pragma once
#ifdef XR_USE_PLATFORM_ANDROID
#include <android/native_window.h>
#include <android/window.h>
#include <android/native_window_jni.h>
#endif // XR_USE_PLATFORM_ANDROID
#ifdef XR_USE_PLATFORM_WIN32
#include <winapifamily.h>
#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM))
// Enable desktop partition APIs, such as RegOpenKeyEx, LoadLibraryEx, PathFileExists etc.
#undef WINAPI_PARTITION_DESKTOP
#define WINAPI_PARTITION_DESKTOP 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif // !NOMINMAX
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif // !WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <unknwn.h>
#endif // XR_USE_PLATFORM_WIN32
#ifdef XR_USE_GRAPHICS_API_D3D11
#include <d3d11.h>
#endif // XR_USE_GRAPHICS_API_D3D11
#ifdef XR_USE_GRAPHICS_API_D3D12
#include <d3d12.h>
#endif // XR_USE_GRAPHICS_API_D3D12
#ifdef XR_USE_PLATFORM_XLIB
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef Success
#undef Success
#endif // Success
#ifdef Always
#undef Always
#endif // Always
#ifdef None
#undef None
#endif // None
#endif // XR_USE_PLATFORM_XLIB
#ifdef XR_USE_PLATFORM_XCB
#include <xcb/xcb.h>
#endif // XR_USE_PLATFORM_XCB
#ifdef XR_USE_GRAPHICS_API_OPENGL
#if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)
#include <GL/glx.h>
#endif // (XR_USE_PLATFORM_XLIB || XR_USE_PLATFORM_XCB)
#ifdef XR_USE_PLATFORM_XCB
#include <xcb/glx.h>
#endif // XR_USE_PLATFORM_XCB
#ifdef XR_USE_PLATFORM_MACOS
#include <CL/cl_gl_ext.h>
#endif // XR_USE_PLATFORM_MACOS
#endif // XR_USE_GRAPHICS_API_OPENGL
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
#include <EGL/egl.h>
#endif // XR_USE_GRAPHICS_API_OPENGL_ES
#ifdef XR_USE_GRAPHICS_API_VULKAN
#include <vulkan/vulkan.h>
#endif // XR_USE_GRAPHICS_API_VULKAN
#ifdef XR_USE_PLATFORM_WAYLAND
#include "wayland-client.h"
#endif // XR_USE_PLATFORM_WAYLAND

787
thirdparty/openxr/src/common/xr_linear.h vendored Normal file
View File

@ -0,0 +1,787 @@
// Copyright (c) 2017 The Khronos Group Inc.
// Copyright (c) 2016 Oculus VR, LLC.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: J.M.P. van Waveren
//
#ifndef XR_LINEAR_H_
#define XR_LINEAR_H_
#if defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) || defined(OS_LINUX_WAYLAND)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#include <openxr/openxr.h>
/*
================================================================================================
Description : Vector, matrix and quaternion math.
Author : J.M.P. van Waveren
Date : 12/10/2016
Language : C99
Format : Indent 4 spaces - no tabs.
Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved.
DESCRIPTION
===========
All matrices are column-major.
INTERFACE
=========
XrVector2f
XrVector3f
XrVector4f
XrQuaternionf
XrMatrix4x4f
inline static void XrVector3f_Set(XrVector3f* v, const float value);
inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);
inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);
inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);
inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);
inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value);
inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction);
inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor);
inline static void XrVector3f_Normalize(XrVector3f* v);
inline static float XrVector3f_Length(const XrVector3f* v);
inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction);
inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b;
inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result);
inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z);
inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,
const float degreesZ);
inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z);
inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation,
const XrQuaternionf* rotation, const XrVector3f* scale);
inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, const float tanAngleLeft, const float tanAngleRight,
const float tanAngleUp, float const tanAngleDown, const float nearZ,
const float farZ);
inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, const float fovDegreesLeft, const float fovDegreesRight,
const float fovDegreeUp, const float fovDegreesDown, const float nearZ,
const float farZ);
inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* src);
inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins,
const XrVector3f* maxs);
inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon);
inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon);
inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon);
inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon);
inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b);
inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src);
inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v);
inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v);
inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix,
const XrVector3f* mins, const XrVector3f* maxs);
inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs);
================================================================================================
*/
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#define MATH_PI 3.14159265358979323846f
#define DEFAULT_NEAR_Z 0.015625f // exact floating point representation
#define INFINITE_FAR_Z 0.0f
static const XrColor4f XrColorRed = {1.0f, 0.0f, 0.0f, 1.0f};
static const XrColor4f XrColorGreen = {0.0f, 1.0f, 0.0f, 1.0f};
static const XrColor4f XrColorBlue = {0.0f, 0.0f, 1.0f, 1.0f};
static const XrColor4f XrColorYellow = {1.0f, 1.0f, 0.0f, 1.0f};
static const XrColor4f XrColorPurple = {1.0f, 0.0f, 1.0f, 1.0f};
static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f};
static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f};
static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f};
enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D };
// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.
struct XrMatrix4x4f {
float m[16];
};
inline static float XrRcpSqrt(const float x) {
const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 )
const float rcp = (x >= SMALLEST_NON_DENORMAL) ? 1.0f / sqrtf(x) : 1.0f;
return rcp;
}
inline static void XrVector3f_Set(XrVector3f* v, const float value) {
v->x = value;
v->y = value;
v->z = value;
}
inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {
result->x = a->x + b->x;
result->y = a->y + b->y;
result->z = a->z + b->z;
}
inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {
result->x = a->x - b->x;
result->y = a->y - b->y;
result->z = a->z - b->z;
}
inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {
result->x = (a->x < b->x) ? a->x : b->x;
result->y = (a->y < b->y) ? a->y : b->y;
result->z = (a->z < b->z) ? a->z : b->z;
}
inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {
result->x = (a->x > b->x) ? a->x : b->x;
result->y = (a->y > b->y) ? a->y : b->y;
result->z = (a->z > b->z) ? a->z : b->z;
}
inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value) {
result->x = (fabsf(a->x) > value) ? ((a->x > 0.0f) ? (a->x - value) : (a->x + value)) : 0.0f;
result->y = (fabsf(a->y) > value) ? ((a->y > 0.0f) ? (a->y - value) : (a->y + value)) : 0.0f;
result->z = (fabsf(a->z) > value) ? ((a->z > 0.0f) ? (a->z - value) : (a->z + value)) : 0.0f;
}
inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction) {
result->x = a->x + fraction * (b->x - a->x);
result->y = a->y + fraction * (b->y - a->y);
result->z = a->z + fraction * (b->z - a->z);
}
inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor) {
result->x = a->x * scaleFactor;
result->y = a->y * scaleFactor;
result->z = a->z * scaleFactor;
}
inline static float XrVector3f_Dot(const XrVector3f* a, const XrVector3f* b) { return a->x * b->x + a->y * b->y + a->z * b->z; }
// Compute cross product, which generates a normal vector.
// Direction vector can be determined by right-hand rule: Pointing index finder in
// direction a and middle finger in direction b, thumb will point in Cross(a, b).
inline static void XrVector3f_Cross(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {
result->x = a->y * b->z - a->z * b->y;
result->y = a->z * b->x - a->x * b->z;
result->z = a->x * b->y - a->y * b->x;
}
inline static void XrVector3f_Normalize(XrVector3f* v) {
const float lengthRcp = XrRcpSqrt(v->x * v->x + v->y * v->y + v->z * v->z);
v->x *= lengthRcp;
v->y *= lengthRcp;
v->z *= lengthRcp;
}
inline static float XrVector3f_Length(const XrVector3f* v) { return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); }
inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians) {
float s = sinf(angleInRadians / 2.0f);
float lengthRcp = XrRcpSqrt(axis->x * axis->x + axis->y * axis->y + axis->z * axis->z);
result->x = s * axis->x * lengthRcp;
result->y = s * axis->y * lengthRcp;
result->z = s * axis->z * lengthRcp;
result->w = cosf(angleInRadians / 2.0f);
}
inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction) {
const float s = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w;
const float fa = 1.0f - fraction;
const float fb = (s < 0.0f) ? -fraction : fraction;
const float x = a->x * fa + b->x * fb;
const float y = a->y * fa + b->y * fb;
const float z = a->z * fa + b->z * fb;
const float w = a->w * fa + b->w * fb;
const float lengthRcp = XrRcpSqrt(x * x + y * y + z * z + w * w);
result->x = x * lengthRcp;
result->y = y * lengthRcp;
result->z = z * lengthRcp;
result->w = w * lengthRcp;
}
inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b) {
result->x = (b->w * a->x) + (b->x * a->w) + (b->y * a->z) - (b->z * a->y);
result->y = (b->w * a->y) - (b->x * a->z) + (b->y * a->w) + (b->z * a->x);
result->z = (b->w * a->z) + (b->x * a->y) - (b->y * a->x) + (b->z * a->w);
result->w = (b->w * a->w) - (b->x * a->x) - (b->y * a->y) - (b->z * a->z);
}
// Use left-multiplication to accumulate transformations.
inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b) {
result->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3];
result->m[1] = a->m[1] * b->m[0] + a->m[5] * b->m[1] + a->m[9] * b->m[2] + a->m[13] * b->m[3];
result->m[2] = a->m[2] * b->m[0] + a->m[6] * b->m[1] + a->m[10] * b->m[2] + a->m[14] * b->m[3];
result->m[3] = a->m[3] * b->m[0] + a->m[7] * b->m[1] + a->m[11] * b->m[2] + a->m[15] * b->m[3];
result->m[4] = a->m[0] * b->m[4] + a->m[4] * b->m[5] + a->m[8] * b->m[6] + a->m[12] * b->m[7];
result->m[5] = a->m[1] * b->m[4] + a->m[5] * b->m[5] + a->m[9] * b->m[6] + a->m[13] * b->m[7];
result->m[6] = a->m[2] * b->m[4] + a->m[6] * b->m[5] + a->m[10] * b->m[6] + a->m[14] * b->m[7];
result->m[7] = a->m[3] * b->m[4] + a->m[7] * b->m[5] + a->m[11] * b->m[6] + a->m[15] * b->m[7];
result->m[8] = a->m[0] * b->m[8] + a->m[4] * b->m[9] + a->m[8] * b->m[10] + a->m[12] * b->m[11];
result->m[9] = a->m[1] * b->m[8] + a->m[5] * b->m[9] + a->m[9] * b->m[10] + a->m[13] * b->m[11];
result->m[10] = a->m[2] * b->m[8] + a->m[6] * b->m[9] + a->m[10] * b->m[10] + a->m[14] * b->m[11];
result->m[11] = a->m[3] * b->m[8] + a->m[7] * b->m[9] + a->m[11] * b->m[10] + a->m[15] * b->m[11];
result->m[12] = a->m[0] * b->m[12] + a->m[4] * b->m[13] + a->m[8] * b->m[14] + a->m[12] * b->m[15];
result->m[13] = a->m[1] * b->m[12] + a->m[5] * b->m[13] + a->m[9] * b->m[14] + a->m[13] * b->m[15];
result->m[14] = a->m[2] * b->m[12] + a->m[6] * b->m[13] + a->m[10] * b->m[14] + a->m[14] * b->m[15];
result->m[15] = a->m[3] * b->m[12] + a->m[7] * b->m[13] + a->m[11] * b->m[14] + a->m[15] * b->m[15];
}
// Creates the transpose of the given matrix.
inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src) {
result->m[0] = src->m[0];
result->m[1] = src->m[4];
result->m[2] = src->m[8];
result->m[3] = src->m[12];
result->m[4] = src->m[1];
result->m[5] = src->m[5];
result->m[6] = src->m[9];
result->m[7] = src->m[13];
result->m[8] = src->m[2];
result->m[9] = src->m[6];
result->m[10] = src->m[10];
result->m[11] = src->m[14];
result->m[12] = src->m[3];
result->m[13] = src->m[7];
result->m[14] = src->m[11];
result->m[15] = src->m[15];
}
// Returns a 3x3 minor of a 4x4 matrix.
inline static float XrMatrix4x4f_Minor(const XrMatrix4x4f* matrix, int r0, int r1, int r2, int c0, int c1, int c2) {
return matrix->m[4 * r0 + c0] *
(matrix->m[4 * r1 + c1] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c1] * matrix->m[4 * r1 + c2]) -
matrix->m[4 * r0 + c1] *
(matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c2]) +
matrix->m[4 * r0 + c2] *
(matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c1] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c1]);
}
// Calculates the inverse of a 4x4 matrix.
inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src) {
const float rcpDet =
1.0f / (src->m[0] * XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) - src->m[1] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) +
src->m[2] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) - src->m[3] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2));
result->m[0] = XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) * rcpDet;
result->m[1] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 1, 2, 3) * rcpDet;
result->m[2] = XrMatrix4x4f_Minor(src, 0, 1, 3, 1, 2, 3) * rcpDet;
result->m[3] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 1, 2, 3) * rcpDet;
result->m[4] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) * rcpDet;
result->m[5] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 2, 3) * rcpDet;
result->m[6] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 2, 3) * rcpDet;
result->m[7] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 2, 3) * rcpDet;
result->m[8] = XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) * rcpDet;
result->m[9] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 3) * rcpDet;
result->m[10] = XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 3) * rcpDet;
result->m[11] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 3) * rcpDet;
result->m[12] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2) * rcpDet;
result->m[13] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 2) * rcpDet;
result->m[14] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 2) * rcpDet;
result->m[15] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 2) * rcpDet;
}
// Calculates the inverse of a rigid body transform.
inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src) {
result->m[0] = src->m[0];
result->m[1] = src->m[4];
result->m[2] = src->m[8];
result->m[3] = 0.0f;
result->m[4] = src->m[1];
result->m[5] = src->m[5];
result->m[6] = src->m[9];
result->m[7] = 0.0f;
result->m[8] = src->m[2];
result->m[9] = src->m[6];
result->m[10] = src->m[10];
result->m[11] = 0.0f;
result->m[12] = -(src->m[0] * src->m[12] + src->m[1] * src->m[13] + src->m[2] * src->m[14]);
result->m[13] = -(src->m[4] * src->m[12] + src->m[5] * src->m[13] + src->m[6] * src->m[14]);
result->m[14] = -(src->m[8] * src->m[12] + src->m[9] * src->m[13] + src->m[10] * src->m[14]);
result->m[15] = 1.0f;
}
// Creates an identity matrix.
inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result) {
result->m[0] = 1.0f;
result->m[1] = 0.0f;
result->m[2] = 0.0f;
result->m[3] = 0.0f;
result->m[4] = 0.0f;
result->m[5] = 1.0f;
result->m[6] = 0.0f;
result->m[7] = 0.0f;
result->m[8] = 0.0f;
result->m[9] = 0.0f;
result->m[10] = 1.0f;
result->m[11] = 0.0f;
result->m[12] = 0.0f;
result->m[13] = 0.0f;
result->m[14] = 0.0f;
result->m[15] = 1.0f;
}
// Creates a translation matrix.
inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z) {
result->m[0] = 1.0f;
result->m[1] = 0.0f;
result->m[2] = 0.0f;
result->m[3] = 0.0f;
result->m[4] = 0.0f;
result->m[5] = 1.0f;
result->m[6] = 0.0f;
result->m[7] = 0.0f;
result->m[8] = 0.0f;
result->m[9] = 0.0f;
result->m[10] = 1.0f;
result->m[11] = 0.0f;
result->m[12] = x;
result->m[13] = y;
result->m[14] = z;
result->m[15] = 1.0f;
}
// Creates a rotation matrix.
// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll.
inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,
const float degreesZ) {
const float sinX = sinf(degreesX * (MATH_PI / 180.0f));
const float cosX = cosf(degreesX * (MATH_PI / 180.0f));
const XrMatrix4x4f rotationX = {{1, 0, 0, 0, 0, cosX, sinX, 0, 0, -sinX, cosX, 0, 0, 0, 0, 1}};
const float sinY = sinf(degreesY * (MATH_PI / 180.0f));
const float cosY = cosf(degreesY * (MATH_PI / 180.0f));
const XrMatrix4x4f rotationY = {{cosY, 0, -sinY, 0, 0, 1, 0, 0, sinY, 0, cosY, 0, 0, 0, 0, 1}};
const float sinZ = sinf(degreesZ * (MATH_PI / 180.0f));
const float cosZ = cosf(degreesZ * (MATH_PI / 180.0f));
const XrMatrix4x4f rotationZ = {{cosZ, sinZ, 0, 0, -sinZ, cosZ, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
XrMatrix4x4f rotationXY;
XrMatrix4x4f_Multiply(&rotationXY, &rotationY, &rotationX);
XrMatrix4x4f_Multiply(result, &rotationZ, &rotationXY);
}
// Creates a scale matrix.
inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z) {
result->m[0] = x;
result->m[1] = 0.0f;
result->m[2] = 0.0f;
result->m[3] = 0.0f;
result->m[4] = 0.0f;
result->m[5] = y;
result->m[6] = 0.0f;
result->m[7] = 0.0f;
result->m[8] = 0.0f;
result->m[9] = 0.0f;
result->m[10] = z;
result->m[11] = 0.0f;
result->m[12] = 0.0f;
result->m[13] = 0.0f;
result->m[14] = 0.0f;
result->m[15] = 1.0f;
}
// Creates a matrix from a quaternion.
inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat) {
const float x2 = quat->x + quat->x;
const float y2 = quat->y + quat->y;
const float z2 = quat->z + quat->z;
const float xx2 = quat->x * x2;
const float yy2 = quat->y * y2;
const float zz2 = quat->z * z2;
const float yz2 = quat->y * z2;
const float wx2 = quat->w * x2;
const float xy2 = quat->x * y2;
const float wz2 = quat->w * z2;
const float xz2 = quat->x * z2;
const float wy2 = quat->w * y2;
result->m[0] = 1.0f - yy2 - zz2;
result->m[1] = xy2 + wz2;
result->m[2] = xz2 - wy2;
result->m[3] = 0.0f;
result->m[4] = xy2 - wz2;
result->m[5] = 1.0f - xx2 - zz2;
result->m[6] = yz2 + wx2;
result->m[7] = 0.0f;
result->m[8] = xz2 + wy2;
result->m[9] = yz2 - wx2;
result->m[10] = 1.0f - xx2 - yy2;
result->m[11] = 0.0f;
result->m[12] = 0.0f;
result->m[13] = 0.0f;
result->m[14] = 0.0f;
result->m[15] = 1.0f;
}
// Creates a combined translation(rotation(scale(object))) matrix.
inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation,
const XrQuaternionf* rotation, const XrVector3f* scale) {
XrMatrix4x4f scaleMatrix;
XrMatrix4x4f_CreateScale(&scaleMatrix, scale->x, scale->y, scale->z);
XrMatrix4x4f rotationMatrix;
XrMatrix4x4f_CreateFromQuaternion(&rotationMatrix, rotation);
XrMatrix4x4f translationMatrix;
XrMatrix4x4f_CreateTranslation(&translationMatrix, translation->x, translation->y, translation->z);
XrMatrix4x4f combinedMatrix;
XrMatrix4x4f_Multiply(&combinedMatrix, &rotationMatrix, &scaleMatrix);
XrMatrix4x4f_Multiply(result, &translationMatrix, &combinedMatrix);
}
// Creates a projection matrix based on the specified dimensions.
// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API.
// The far plane is placed at infinity if farZ <= nearZ.
// An infinite projection matrix is preferred for rasterization because, except for
// things *right* up against the near plane, it always provides better precision:
// "Tightening the Precision of Perspective Rendering"
// Paul Upchurch, Mathieu Desbrun
// Journal of Graphics Tools, Volume 16, Issue 1, 2012
inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft,
const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
const float nearZ, const float farZ) {
const float tanAngleWidth = tanAngleRight - tanAngleLeft;
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan).
// Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal).
const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
if (farZ <= nearZ) {
// place the far plane at infinity
result->m[0] = 2.0f / tanAngleWidth;
result->m[4] = 0.0f;
result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
result->m[12] = 0.0f;
result->m[1] = 0.0f;
result->m[5] = 2.0f / tanAngleHeight;
result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
result->m[13] = 0.0f;
result->m[2] = 0.0f;
result->m[6] = 0.0f;
result->m[10] = -1.0f;
result->m[14] = -(nearZ + offsetZ);
result->m[3] = 0.0f;
result->m[7] = 0.0f;
result->m[11] = -1.0f;
result->m[15] = 0.0f;
} else {
// normal projection
result->m[0] = 2.0f / tanAngleWidth;
result->m[4] = 0.0f;
result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
result->m[12] = 0.0f;
result->m[1] = 0.0f;
result->m[5] = 2.0f / tanAngleHeight;
result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
result->m[13] = 0.0f;
result->m[2] = 0.0f;
result->m[6] = 0.0f;
result->m[10] = -(farZ + offsetZ) / (farZ - nearZ);
result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
result->m[3] = 0.0f;
result->m[7] = 0.0f;
result->m[11] = -1.0f;
result->m[15] = 0.0f;
}
}
// Creates a projection matrix based on the specified FOV.
inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov,
const float nearZ, const float farZ) {
const float tanLeft = tanf(fov.angleLeft);
const float tanRight = tanf(fov.angleRight);
const float tanDown = tanf(fov.angleDown);
const float tanUp = tanf(fov.angleUp);
XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ);
}
// Creates a matrix that transforms the -1 to 1 cube to cover the given 'mins' and 'maxs' transformed with the given 'matrix'.
inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins,
const XrVector3f* maxs) {
const XrVector3f offset = {(maxs->x + mins->x) * 0.5f, (maxs->y + mins->y) * 0.5f, (maxs->z + mins->z) * 0.5f};
const XrVector3f scale = {(maxs->x - mins->x) * 0.5f, (maxs->y - mins->y) * 0.5f, (maxs->z - mins->z) * 0.5f};
result->m[0] = matrix->m[0] * scale.x;
result->m[1] = matrix->m[1] * scale.x;
result->m[2] = matrix->m[2] * scale.x;
result->m[3] = matrix->m[3] * scale.x;
result->m[4] = matrix->m[4] * scale.y;
result->m[5] = matrix->m[5] * scale.y;
result->m[6] = matrix->m[6] * scale.y;
result->m[7] = matrix->m[7] * scale.y;
result->m[8] = matrix->m[8] * scale.z;
result->m[9] = matrix->m[9] * scale.z;
result->m[10] = matrix->m[10] * scale.z;
result->m[11] = matrix->m[11] * scale.z;
result->m[12] = matrix->m[12] + matrix->m[0] * offset.x + matrix->m[4] * offset.y + matrix->m[8] * offset.z;
result->m[13] = matrix->m[13] + matrix->m[1] * offset.x + matrix->m[5] * offset.y + matrix->m[9] * offset.z;
result->m[14] = matrix->m[14] + matrix->m[2] * offset.x + matrix->m[6] * offset.y + matrix->m[10] * offset.z;
result->m[15] = matrix->m[15] + matrix->m[3] * offset.x + matrix->m[7] * offset.y + matrix->m[11] * offset.z;
}
// Returns true if the given matrix is affine.
inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon) {
return fabsf(matrix->m[3]) <= epsilon && fabsf(matrix->m[7]) <= epsilon && fabsf(matrix->m[11]) <= epsilon &&
fabsf(matrix->m[15] - 1.0f) <= epsilon;
}
// Returns true if the given matrix is orthogonal.
inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i != j) {
if (fabsf(matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] +
matrix->m[4 * i + 2] * matrix->m[4 * j + 2]) > epsilon) {
return false;
}
if (fabsf(matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] +
matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j]) > epsilon) {
return false;
}
}
}
}
return true;
}
// Returns true if the given matrix is orthonormal.
inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
const float kd = (i == j) ? 1.0f : 0.0f; // Kronecker delta
if (fabsf(kd - (matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] +
matrix->m[4 * i + 2] * matrix->m[4 * j + 2])) > epsilon) {
return false;
}
if (fabsf(kd - (matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] +
matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j])) > epsilon) {
return false;
}
}
}
return true;
}
// Returns true if the given matrix is a rigid body transform.
inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon) {
return XrMatrix4x4f_IsAffine(matrix, epsilon) && XrMatrix4x4f_IsOrthonormal(matrix, epsilon);
}
// Get the translation from a combined translation(rotation(scale(object))) matrix.
inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src) {
assert(XrMatrix4x4f_IsAffine(src, 1e-4f));
assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));
result->x = src->m[12];
result->y = src->m[13];
result->z = src->m[14];
}
// Get the rotation from a combined translation(rotation(scale(object))) matrix.
inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src) {
assert(XrMatrix4x4f_IsAffine(src, 1e-4f));
assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));
const float rcpScaleX = XrRcpSqrt(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]);
const float rcpScaleY = XrRcpSqrt(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]);
const float rcpScaleZ = XrRcpSqrt(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]);
const float m[9] = {src->m[0] * rcpScaleX, src->m[1] * rcpScaleX, src->m[2] * rcpScaleX,
src->m[4] * rcpScaleY, src->m[5] * rcpScaleY, src->m[6] * rcpScaleY,
src->m[8] * rcpScaleZ, src->m[9] * rcpScaleZ, src->m[10] * rcpScaleZ};
if (m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] > 0.0f) {
float t = +m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f;
float s = XrRcpSqrt(t) * 0.5f;
result->w = s * t;
result->z = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s;
result->y = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s;
result->x = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s;
} else if (m[0 * 3 + 0] > m[1 * 3 + 1] && m[0 * 3 + 0] > m[2 * 3 + 2]) {
float t = +m[0 * 3 + 0] - m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f;
float s = XrRcpSqrt(t) * 0.5f;
result->x = s * t;
result->y = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s;
result->z = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s;
result->w = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s;
} else if (m[1 * 3 + 1] > m[2 * 3 + 2]) {
float t = -m[0 * 3 + 0] + m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f;
float s = XrRcpSqrt(t) * 0.5f;
result->y = s * t;
result->x = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s;
result->w = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s;
result->z = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s;
} else {
float t = -m[0 * 3 + 0] - m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f;
float s = XrRcpSqrt(t) * 0.5f;
result->z = s * t;
result->w = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s;
result->x = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s;
result->y = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s;
}
}
// Get the scale from a combined translation(rotation(scale(object))) matrix.
inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src) {
assert(XrMatrix4x4f_IsAffine(src, 1e-4f));
assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));
result->x = sqrtf(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]);
result->y = sqrtf(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]);
result->z = sqrtf(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]);
}
// Transforms a 3D vector.
inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v) {
const float w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15];
const float rcpW = 1.0f / w;
result->x = (m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12]) * rcpW;
result->y = (m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13]) * rcpW;
result->z = (m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14]) * rcpW;
}
// Transforms a 4D vector.
inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v) {
result->x = m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12] * v->w;
result->y = m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13] * v->w;
result->z = m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14] * v->w;
result->w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15] * v->w;
}
// Transforms the 'mins' and 'maxs' bounds with the given 'matrix'.
inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix,
const XrVector3f* mins, const XrVector3f* maxs) {
assert(XrMatrix4x4f_IsAffine(matrix, 1e-4f));
const XrVector3f center = {(mins->x + maxs->x) * 0.5f, (mins->y + maxs->y) * 0.5f, (mins->z + maxs->z) * 0.5f};
const XrVector3f extents = {maxs->x - center.x, maxs->y - center.y, maxs->z - center.z};
const XrVector3f newCenter = {matrix->m[0] * center.x + matrix->m[4] * center.y + matrix->m[8] * center.z + matrix->m[12],
matrix->m[1] * center.x + matrix->m[5] * center.y + matrix->m[9] * center.z + matrix->m[13],
matrix->m[2] * center.x + matrix->m[6] * center.y + matrix->m[10] * center.z + matrix->m[14]};
const XrVector3f newExtents = {
fabsf(extents.x * matrix->m[0]) + fabsf(extents.y * matrix->m[4]) + fabsf(extents.z * matrix->m[8]),
fabsf(extents.x * matrix->m[1]) + fabsf(extents.y * matrix->m[5]) + fabsf(extents.z * matrix->m[9]),
fabsf(extents.x * matrix->m[2]) + fabsf(extents.y * matrix->m[6]) + fabsf(extents.z * matrix->m[10])};
XrVector3f_Sub(resultMins, &newCenter, &newExtents);
XrVector3f_Add(resultMaxs, &newCenter, &newExtents);
}
// Returns true if the 'mins' and 'maxs' bounds is completely off to one side of the projection matrix.
inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs) {
if (maxs->x <= mins->x && maxs->y <= mins->y && maxs->z <= mins->z) {
return false;
}
XrVector4f c[8];
for (int i = 0; i < 8; i++) {
const XrVector4f corner = {(i & 1) != 0 ? maxs->x : mins->x, (i & 2) != 0 ? maxs->y : mins->y,
(i & 4) != 0 ? maxs->z : mins->z, 1.0f};
XrMatrix4x4f_TransformVector4f(&c[i], mvp, &corner);
}
int i;
for (i = 0; i < 8; i++) {
if (c[i].x > -c[i].w) {
break;
}
}
if (i == 8) {
return true;
}
for (i = 0; i < 8; i++) {
if (c[i].x < c[i].w) {
break;
}
}
if (i == 8) {
return true;
}
for (i = 0; i < 8; i++) {
if (c[i].y > -c[i].w) {
break;
}
}
if (i == 8) {
return true;
}
for (i = 0; i < 8; i++) {
if (c[i].y < c[i].w) {
break;
}
}
if (i == 8) {
return true;
}
for (i = 0; i < 8; i++) {
if (c[i].z > -c[i].w) {
break;
}
}
if (i == 8) {
return true;
}
for (i = 0; i < 8; i++) {
if (c[i].z < c[i].w) {
break;
}
}
return i == 8;
}
#endif // XR_LINEAR_H_

View File

@ -0,0 +1,115 @@
Baptiste Lepilleur <blep@users.sourceforge.net>
Aaron Jacobs <aaronjjacobs@gmail.com>
Aaron Jacobs <jacobsa@google.com>
Adam Boseley <ABoseley@agjunction.com>
Adam Boseley <adam.boseley@gmail.com>
Aleksandr Derbenev <13alexac@gmail.com>
Alexander Gazarov <DrMetallius@users.noreply.github.com>
Alexander V. Brezgin <abrezgin@appliedtech.ru>
Alexandr Brezgin <albrezgin@mail.ru>
Alexey Kruchinin <alexey@mopals.com>
Anton Indrawan <anton.indrawan@gmail.com>
Baptiste Jonglez <git@bitsofnetworks.org>
Baptiste Lepilleur <baptiste.lepilleur@gmail.com>
Baruch Siach <baruch@tkos.co.il>
Ben Boeckel <mathstuf@gmail.com>
Benjamin Knecht <bknecht@logitech.com>
Bernd Kuhls <bernd.kuhls@t-online.de>
Billy Donahue <billydonahue@google.com>
Braden McDorman <bmcdorman@gmail.com>
Brandon Myers <bmyers1788@gmail.com>
Brendan Drew <brendan.drew@daqri.com>
chason <cxchao802@gmail.com>
chenguoping <chenguopingdota@163.com>
Chris Gilling <cgilling@iparadigms.com>
Christopher Dawes <christopher.dawes.1981@googlemail.com>
Christopher Dunn <cdunn2001@gmail.com>
Chuck Atkins <chuck.atkins@kitware.com>
Cody P Schafer <dev@codyps.com>
Connor Manning <connor@hobu.co>
Cory Quammen <cory.quammen@kitware.com>
Cristóvão B da Cruz e Silva <CrisXed@gmail.com>
Daniel Krügler <daniel.kruegler@gmail.com>
Dani-Hub <daniel.kruegler@googlemail.com>
Dan Liu <gzliudan>
datadiode <datadiode@users.noreply.github.com>
datadiode <jochen.neubeck@vodafone.de>
David Seifert <soap@gentoo.org>
David West <david-west@idexx.com>
dawesc <chris.dawes@eftlab.co.uk>
Devin Jeanpierre <jeanpierreda@google.com>
Dmitry Marakasov <amdmi3@amdmi3.ru>
dominicpezzuto <dom@dompezzuto.com>
Don Milham <dmilham@gmail.com>
drgler <daniel.kruegler@gmail.com>
ds283 <D.Seery@sussex.ac.uk>
Egor Tensin <Egor.Tensin@gmail.com>
eightnoteight <mr.eightnoteight@gmail.com>
Evince <baneyue@gmail.com>
filipjs <filipjs@users.noreply.github.com>
findblar <ft@finbarr.ca>
Florian Meier <florian.meier@koalo.de>
Gaëtan Lehmann <gaetan.lehmann@gmail.com>
Gaurav <g.gupta@samsung.com>
Gergely Nagy <ngg@ngg.hu>
Gida Pataki <gida.pataki@prezi.com>
I3ck <buckmartin@buckmartin.de>
Iñaki Baz Castillo <ibc@aliax.net>
Jacco <jacco@geul.net>
Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
Jonas Platte <mail@jonasplatte.de>
Jordan Bayles <bayles.jordan@gmail.com>
Jörg Krause <joerg.krause@embedded.rocks>
Keith Lea <keith@whamcitylights.com>
Kevin Grant <kbradleygrant@gmail.com>
Kirill V. Lyadvinsky <jia3ep@gmail.com>
Kirill V. Lyadvinsky <mail@codeatcpp.com>
Kobi Gurkan <kobigurk@gmail.com>
Magnus Bjerke Vik <mbvett@gmail.com>
Malay Shah <malays@users.sourceforge.net>
Mara Kim <hacker.root@gmail.com>
Marek Kotewicz <marek.kotewicz@gmail.com>
Mark Lakata <mark@lakata.org>
Mark Zeren <mzeren@vmware.com>
Martin Buck <buckmartin@buckmartin.de>
Martyn Gigg <martyn.gigg@gmail.com>
Mattes D <github@xoft.cz>
Matthias Loy <matthias.loy@hbm.com>
Merlyn Morgan-Graham <kavika@gmail.com>
Michael Shields <mshields@google.com>
Michał Górny <mgorny@gentoo.org>
Mike Naberezny <mike@naberezny.com>
mloy <matthias.loy@googlemail.com>
Motti <lanzkron@gmail.com>
nnkur <nnkur@mail.ru>
Omkar Wagh <owagh@owaghlinux.ny.tower-research.com>
paulo <paulobrizolara@users.noreply.github.com>
pavel.pimenov <pavel.pimenov@gmail.com>
Paweł Bylica <chfast@gmail.com>
Péricles Lopes Machado <pericles.raskolnikoff@gmail.com>
Peter Spiess-Knafl <psk@autistici.org>
pffang <pffang@vip.qq.com>
Rémi Verschelde <remi@verschelde.fr>
renu555 <renu.tyagi@samsung.com>
Robert Dailey <rcdailey@gmail.com>
Sam Clegg <sbc@chromium.org>
selaselah <selah@outlook.com>
Sergiy80 <sil2004@gmail.com>
sergzub <sergzub@gmail.com>
Stefan Schweter <stefan@schweter.it>
Stefano Fiorentino <stefano.fiore84@gmail.com>
Steffen Kieß <Steffen.Kiess@ipvs.uni-stuttgart.de>
Steven Hahn <hahnse@ornl.gov>
Stuart Eichert <stuart@fivemicro.com>
SuperManitu <supermanitu@gmail.com>
Techwolf <dring@g33kworld.net>
Tengiz Sharafiev <btolfa+github@gmail.com>
Tomasz Maciejewski <tmaciejewsk@gmail.com>
Vicente Olivert Riera <Vincent.Riera@imgtec.com>
xiaoyur347 <xiaoyur347@gmail.com>
ycqiu <429148848@qq.com>
yiqiju <fred_ju@selinc.com>
Yu Xiaolei <dreifachstein@gmail.com>
Google Inc.

View File

@ -0,0 +1,55 @@
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
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.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.

View File

@ -0,0 +1,88 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ALLOCATOR_H_INCLUDED
#define JSON_ALLOCATOR_H_INCLUDED
#include <cstring>
#include <memory>
#pragma pack(push, 8)
namespace Json {
template <typename T> class SecureAllocator {
public:
// Type definitions
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
/**
* Allocate memory for N items using the standard allocator.
*/
pointer allocate(size_type n) {
// allocate using "global operator new"
return static_cast<pointer>(::operator new(n * sizeof(T)));
}
/**
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
*/
void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}
/**
* Construct an item in-place at pointer P.
*/
template <typename... Args> void construct(pointer p, Args&&... args) {
// construct using "placement new" and "perfect forwarding"
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
size_type max_size() const { return size_t(-1) / sizeof(T); }
pointer address(reference x) const { return std::addressof(x); }
const_pointer address(const_reference x) const { return std::addressof(x); }
/**
* Destroy an item in-place at pointer P.
*/
void destroy(pointer p) {
// destroy using "explicit destructor"
p->~T();
}
// Boilerplate
SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; };
};
template <typename T, typename U>
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return true;
}
template <typename T, typename U>
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return false;
}
} // namespace Json
#pragma pack(pop)
#endif // JSON_ALLOCATOR_H_INCLUDED

View File

@ -0,0 +1,61 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ASSERTIONS_H_INCLUDED
#define JSON_ASSERTIONS_H_INCLUDED
#include <cstdlib>
#include <sstream>
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
/** It should not be possible for a maliciously designed file to
* cause an abort() or seg-fault, so these macros are used only
* for pre-condition violations and internal logic errors.
*/
#if JSON_USE_EXCEPTION
// @todo <= add detail about condition in exception
#define JSON_ASSERT(condition) \
do { \
if (!(condition)) { \
Json::throwLogicError("assert json failed"); \
} \
} while (0)
#define JSON_FAIL_MESSAGE(message) \
do { \
OStringStream oss; \
oss << message; \
Json::throwLogicError(oss.str()); \
abort(); \
} while (0)
#else // JSON_USE_EXCEPTION
#define JSON_ASSERT(condition) assert(condition)
// The call to assert() will show the failure message in debug builds. In
// release builds we abort, for a core-dump or debugger.
#define JSON_FAIL_MESSAGE(message) \
{ \
OStringStream oss; \
oss << message; \
assert(false && oss.str().c_str()); \
abort(); \
}
#endif
#define JSON_ASSERT_MESSAGE(condition, message) \
do { \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message); \
} \
} while (0)
#endif // JSON_ASSERTIONS_H_INCLUDED

View File

@ -0,0 +1,150 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
// Temporary, tracked for removal with issue #982.
#ifndef JSON_USE_NULLREF
#define JSON_USE_NULLREF 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
// Export macros for DLL visibility
#if defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_DLL_BUILD
#if !defined(JSON_API)
#define JSON_API
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
#error \
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
// C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
// MSVC)
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "allocator.h"
#include "version.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
using Int = int;
using UInt = unsigned int;
#if defined(JSON_NO_INT64)
using LargestInt = int;
using LargestUInt = unsigned int;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
using Int64 = __int64;
using UInt64 = unsigned __int64;
#else // if defined(_MSC_VER) // Other platforms, use long long
using Int64 = int64_t;
using UInt64 = uint64_t;
#endif // if defined(_MSC_VER)
using LargestInt = Int64;
using LargestUInt = UInt64;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
template <typename T>
using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream =
std::basic_istringstream<String::value_type, String::traits_type,
String::allocator_type>;
using OStringStream =
std::basic_ostringstream<String::value_type, String::traits_type,
String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
// Legacy names (formerly macros).
using JSONCPP_STRING = Json::String;
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
using JSONCPP_ISTREAM = Json::IStream;
using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED

View File

@ -0,0 +1,43 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class StreamWriter;
class StreamWriterBuilder;
class Writer;
class FastWriter;
class StyledWriter;
class StyledStreamWriter;
// reader.h
class Reader;
class CharReader;
class CharReaderBuilder;
// json_features.h
class Features;
// value.h
using ArrayIndex = unsigned int;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED

View File

@ -0,0 +1,15 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED
#include "config.h"
#include "json_features.h"
#include "reader.h"
#include "value.h"
#include "writer.h"
#endif // JSON_JSON_H_INCLUDED

View File

@ -0,0 +1,61 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FEATURES_H_INCLUDED
#define JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#pragma pack(push, 8)
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features {
public:
/** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_{true};
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_{false};
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_{false};
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_{false};
};
} // namespace Json
#pragma pack(pop)
#endif // JSON_FEATURES_H_INCLUDED

View File

@ -0,0 +1,405 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_READER_H_INCLUDED
#define JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "json_features.h"
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque>
#include <iosfwd>
#include <istream>
#include <stack>
#include <string>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
* Value.
*
* \deprecated Use CharReader and CharReaderBuilder.
*/
class JSON_API Reader {
public:
using Char = char;
using Location = const Char*;
/** \brief An error tagged with where in the JSON text it was encountered.
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
*/
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
/** \brief Constructs a Reader allowing all features for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
Reader();
/** \brief Constructs a Reader allowing the specified feature set for parsing.
* \deprecated Use CharReader and CharReaderBuilder.
*/
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param document UTF-8 encoded string containing the document
* to read.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const std::string& document, Value& root,
bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded
* string of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string
* of the document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse(IStream& is, Value& root, bool collectComments = true);
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
String getFormatedErrorMessages() const;
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
*/
String getFormattedErrorMessages() const;
/** \brief Returns a vector of structured errors encountered while parsing.
*
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate multiple
* errors. This can occur if the parser recovers from a non-fatal parse
* error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \return \c true if the error was successfully added, \c false if the Value
* offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message);
/** \brief Add a semantic error message with extra context.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message, const Value& extra);
/** \brief Return whether there are any errors.
*
* \return \c true if there are no errors to report \c false if errors have
* occurred.
*/
bool good() const;
private:
enum TokenType {
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token {
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo {
public:
Token token_;
String message_;
Location extra_;
};
using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token);
void skipSpaces();
bool match(const Char* pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject(Token& token);
bool readArray(Token& token);
bool decodeNumber(Token& token);
bool decodeNumber(Token& token, Value& decoded);
bool decodeString(Token& token);
bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token, Location& current,
Location end, unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
void getLocationLineAndColumn(Location location, int& line,
int& column) const;
String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static bool containsNewLine(Location begin, Location end);
static String normalizeEOL(Location begin, Location end);
using Nodes = std::stack<Value*>;
Nodes nodes_;
Errors errors_;
String document_;
Location begin_{};
Location end_{};
Location current_{};
Location lastValueEnd_{};
Value* lastValue_{};
String commentsBefore_;
Features features_;
bool collectComments_{};
}; // Reader
/** Interface for reading JSON from a char array.
*/
class JSON_API CharReader {
public:
virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the
* document to read.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string
* of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
* document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it was
* successfully parsed.
* \param[out] errs Formatted error messages (if not NULL) a user
* friendly string that lists errors in the parsed
* document.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
class JSON_API Factory {
public:
virtual ~Factory() = default;
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
}; // CharReader
/** \brief Build a CharReader implementation.
*
* Usage:
* \code
* using namespace Json;
* CharReaderBuilder builder;
* builder["collectComments"] = false;
* Value value;
* String errs;
* bool ok = parseFromStream(builder, std::cin, &value, &errs);
* \endcode
*/
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* These are case-sensitive.
* Available settings (case-sensitive):
* - `"collectComments": false or true`
* - true to collect comment and allow writing them back during
* serialization, false to discard comments. This parameter is ignored
* if allowComments is false.
* - `"allowComments": false or true`
* - true if comments are allowed.
* - `"allowTrailingCommas": false or true`
* - true if trailing commas in objects and arrays are allowed.
* - `"strictRoot": false or true`
* - true if root must be either an array or an object value
* - `"allowDroppedNullPlaceholders": false or true`
* - true if dropped null placeholders are allowed. (See
* StreamWriterBuilder.)
* - `"allowNumericKeys": false or true`
* - true if numeric object keys are allowed.
* - `"allowSingleQuotes": false or true`
* - true if '' are allowed for strings (both keys and values)
* - `"stackLimit": integer`
* - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
* exception.
* - This is a security issue (seg-faults caused by deeply nested JSON), so
* the default is low.
* - `"failIfExtra": false or true`
* - If true, `parse()` returns false when extra non-whitespace trails the
* JSON value in the input string.
* - `"rejectDupKeys": false or true`
* - If true, `parse()` returns false when a key is duplicated within an
* object.
* - `"allowSpecialFloats": false or true`
* - If true, special float values (NaNs and infinities) are allowed and
* their values are lossfree restorable.
* - `"skipBom": false or true`
* - If true, if the input starts with the Unicode byte order mark (BOM),
* it is skipped.
*
* You can examine 'settings_` yourself to see the defaults. You can also
* write and read them just like any JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
CharReaderBuilder();
~CharReaderBuilder() override;
CharReader* newCharReader() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
/** Same as old Features::strictMode().
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
*/
static void strictMode(Json::Value* settings);
};
/** Consume entire stream and use its begin/end.
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
String* errs);
/** \brief Read from 'sin' into 'root'.
*
* Always keep comments from the input JSON.
*
* This can be used to read a file into a particular sub-object.
* For example:
* \code
* Json::Value root;
* cin >> root["dir"]["file"];
* cout << root;
* \endcode
* Result:
* \verbatim
* {
* "dir": {
* "file": {
* // The input stream JSON would be nested here.
* }
* }
* }
* \endverbatim
* \throw std::exception on parse error.
* \see Json::operator<<()
*/
JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_READER_H_INCLUDED

View File

@ -0,0 +1,935 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_H_INCLUDED
#define JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
// Conditional NORETURN attribute on the throw functions would:
// a) suppress false positives from static code analysis
// b) possibly improve optimization opportunities.
#if !defined(JSONCPP_NORETURN)
#if defined(_MSC_VER) && _MSC_VER == 1800
#define JSONCPP_NORETURN __declspec(noreturn)
#else
#define JSONCPP_NORETURN [[noreturn]]
#endif
#endif
// Support for '= delete' with template declarations was a late addition
// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
// even though these declare themselves to be c++11 compilers.
#if !defined(JSONCPP_TEMPLATE_DELETE)
#if defined(__clang__) && defined(__apple_build_version__)
#if __apple_build_version__ <= 8000042
#define JSONCPP_TEMPLATE_DELETE
#endif
#elif defined(__clang__)
#if __clang_major__ == 3 && __clang_minor__ <= 8
#define JSONCPP_TEMPLATE_DELETE
#endif
#endif
#if !defined(JSONCPP_TEMPLATE_DELETE)
#define JSONCPP_TEMPLATE_DELETE = delete
#endif
#endif
#include <array>
#include <exception>
#include <map>
#include <memory>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
#if JSON_USE_EXCEPTION
/** Base class for all exceptions we throw.
*
* We use nothing but these internally. Of course, STL can throw others.
*/
class JSON_API Exception : public std::exception {
public:
Exception(String msg);
~Exception() noexcept override;
char const* what() const noexcept override;
protected:
String msg_;
};
/** Exceptions which the user cannot easily avoid.
*
* E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
*
* \remark derived from Json::Exception
*/
class JSON_API RuntimeError : public Exception {
public:
RuntimeError(String const& msg);
};
/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
*
* These are precondition-violations (user bugs) and internal errors (our bugs).
*
* \remark derived from Json::Exception
*/
class JSON_API LogicError : public Exception {
public:
LogicError(String const& msg);
};
#endif
/// used internally
JSONCPP_NORETURN void throwRuntimeError(String const& msg);
/// used internally
JSONCPP_NORETURN void throwLogicError(String const& msg);
/** \brief Type of the value held by a Value object.
*/
enum ValueType {
nullValue = 0, ///< 'null' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< array value (ordered list)
objectValue ///< object value (collection of name/value pairs).
};
enum CommentPlacement {
commentBefore = 0, ///< a comment placed on the line before a value
commentAfterOnSameLine, ///< a comment just after a value on the same line
commentAfter, ///< a comment on the line after a value (only make sense for
/// root value)
numberOfCommentPlacement
};
/** \brief Type of precision for formatting of real values.
*/
enum PrecisionType {
significantDigits = 0, ///< we set max number of significant digits in string
decimalPlaces ///< we set max number of digits after "." in string
};
/** \brief Lightweight wrapper to tag static string.
*
* Value constructor and objectValue member assignment takes advantage of the
* StaticString and avoid the cost of string duplication when storing the
* string or the member name.
*
* Example of usage:
* \code
* Json::Value aValue( StaticString("some text") );
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
class JSON_API StaticString {
public:
explicit StaticString(const char* czstring) : c_str_(czstring) {}
operator const char*() const { return c_str_; }
const char* c_str() const { return c_str_; }
private:
const char* c_str_;
};
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
*
* This class is a discriminated union wrapper that can represents a:
* - signed integer [range: Value::minInt - Value::maxInt]
* - unsigned integer (range: 0 - Value::maxUInt)
* - double
* - UTF-8 string
* - boolean
* - 'null'
* - an ordered list of Value
* - collection of name/value pairs (javascript object)
*
* The type of the held value is represented by a #ValueType and
* can be obtained using type().
*
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
* methods.
* Non-const methods will automatically create the a #nullValue element
* if it does not exist.
* The sequence of an #arrayValue will be automatically resized and initialized
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
*
* The get() methods can be used to obtain default value in the case the
* required element does not exist.
*
* It is possible to iterate over the list of member keys of an object using
* the getMemberNames() method.
*
* \note #Value string-length fit in size_t, but keys must be < 2^30.
* (The reason is an implementation detail.) A #CharReader will raise an
* exception if a bound is exceeded to avoid security holes in your app,
* but the Value API does *not* check bounds. That is the responsibility
* of the caller.
*/
class JSON_API Value {
friend class ValueIteratorBase;
public:
using Members = std::vector<String>;
using iterator = ValueIterator;
using const_iterator = ValueConstIterator;
using UInt = Json::UInt;
using Int = Json::Int;
#if defined(JSON_HAS_INT64)
using UInt64 = Json::UInt64;
using Int64 = Json::Int64;
#endif // defined(JSON_HAS_INT64)
using LargestInt = Json::LargestInt;
using LargestUInt = Json::LargestUInt;
using ArrayIndex = Json::ArrayIndex;
// Required for boost integration, e. g. BOOST_TEST
using value_type = std::string;
#if JSON_USE_NULLREF
// Binary compatibility kludges, do not use.
static const Value& null;
static const Value& nullRef;
#endif
// null and nullRef are deprecated, use this instead.
static Value const& nullSingleton();
/// Minimum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt minLargestInt =
LargestInt(~(LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value.
static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
/// Minimum signed int value that can be stored in a Json::Value.
static constexpr Int minInt = Int(~(UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value.
static constexpr Int maxInt = Int(UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value.
static constexpr UInt maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64)
/// Default precision for real value for string representation.
static constexpr UInt defaultRealPrecision = 17;
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486
#ifdef __NVCC__
public:
#else
private:
#endif
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
class CZString {
public:
enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
CZString(ArrayIndex index);
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
CZString(CZString const& other);
CZString(CZString&& other) noexcept;
~CZString();
CZString& operator=(const CZString& other);
CZString& operator=(CZString&& other) noexcept;
bool operator<(CZString const& other) const;
bool operator==(CZString const& other) const;
ArrayIndex index() const;
// const char* c_str() const; ///< \deprecated
char const* data() const;
unsigned length() const;
bool isStaticString() const;
private:
void swap(CZString& other);
struct StringStorage {
unsigned policy_ : 2;
unsigned length_ : 30; // 1GB max
};
char const* cstr_; // actually, a prefixed string, unless policy is noDup
union {
ArrayIndex index_;
StringStorage storage_;
};
};
public:
typedef std::map<CZString, Value> ObjectValues;
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public:
/**
* \brief Create a default Value of the given type.
*
* This is a very useful constructor.
* To create an empty array, pass arrayValue.
* To create an empty object, pass objectValue.
* Another Value can then be set to this one by assignment.
* This is useful since clear() and resize() will not alter types.
*
* Examples:
* \code
* Json::Value null_value; // null
* Json::Value arr_value(Json::arrayValue); // []
* Json::Value obj_value(Json::objectValue); // {}
* \endcode
*/
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
#if defined(JSON_HAS_INT64)
Value(Int64 value);
Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
/**
* \brief Constructs a value from a static string.
*
* Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to
* this constructor.
*
* \note This works only for null-terminated strings. (We cannot change the
* size of this class, so we have nowhere to store the length, which might be
* computed later for various operations.)
*
* Example of usage:
* \code
* static StaticString foo("some text");
* Json::Value aValue(foo);
* \endcode
*/
Value(const StaticString& value);
Value(const String& value);
Value(bool value);
Value(std::nullptr_t ptr) = delete;
Value(const Value& other);
Value(Value&& other) noexcept;
~Value();
/// \note Overwrite existing comments. To preserve comments, use
/// #swapPayload().
Value& operator=(const Value& other);
Value& operator=(Value&& other) noexcept;
/// Swap everything.
void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
void swapPayload(Value& other);
/// copy everything.
void copy(const Value& other);
/// copy values but leave comments and source offsets in place.
void copyPayload(const Value& other);
ValueType type() const;
/// Compare payload only, not comments etc.
bool operator<(const Value& other) const;
bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const;
bool operator>(const Value& other) const;
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
int compare(const Value& other) const;
const char* asCString() const; ///< Embedded zeroes could cause you trouble!
#if JSONCPP_USING_SECURE_MEMORY
unsigned getCStringLength() const; // Allows you to understand the length of
// the CString
#endif
String asString() const; ///< Embedded zeroes are possible.
/** Get raw char* of string-value.
* \return false if !string. (Seg-fault if str or end are NULL.)
*/
bool getString(char const** begin, char const** end) const;
Int asInt() const;
UInt asUInt() const;
#if defined(JSON_HAS_INT64)
Int64 asInt64() const;
UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
float asFloat() const;
double asDouble() const;
bool asBool() const;
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;
bool isString() const;
bool isArray() const;
bool isObject() const;
/// The `as<T>` and `is<T>` member function templates and specializations.
template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object
ArrayIndex size() const;
/// \brief Return true if empty array, empty object, or null;
/// otherwise, false.
bool empty() const;
/// Return !isNull()
explicit operator bool() const;
/// Remove all object members and array elements.
/// \pre type() is arrayValue, objectValue, or nullValue
/// \post type() is unchanged
void clear();
/// Resize the array to newSize elements.
/// New elements are initialized to null.
/// May only be called on nullValue or arrayValue.
/// \pre type() is arrayValue or nullValue
/// \post type() is arrayValue
void resize(ArrayIndex newSize);
//@{
/// Access an array element (zero based index). If the array contains less
/// than index element, then null value are inserted in the array so that
/// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
Value& operator[](int index);
//@}
//@{
/// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
//@}
/// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size().
bool isValidIndex(ArrayIndex index) const;
/// \brief Append value to array at the end.
///
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value& append(const Value& value);
Value& append(Value&& value);
/// \brief Insert value in array at specific index
bool insert(ArrayIndex index, const Value& newValue);
bool insert(ArrayIndex index, Value&& newValue);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not exist.
/// \param key may contain embedded nulls.
Value& operator[](const String& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const String& key) const;
/** \brief Access an object value by name, create a null member if it does not
* exist.
*
* If the object has no entry for that name, then the member name used to
* store the new entry is not duplicated.
* Example of use:
* \code
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
Value& operator[](const StaticString& key);
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
Value get(const char* key, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \note key may contain embedded nulls.
Value get(const char* begin, const char* end,
const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const String& key, const Value& defaultValue) const;
/// Most general and efficient version of isMember()const, get()const,
/// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
Value const* find(char const* begin, char const* end) const;
/// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
Value* demand(char const* begin, char const* end);
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
/// \pre type() is objectValue or nullValue
/// \post type() is unchanged
void removeMember(const char* key);
/// Same as removeMember(const char*)
/// \param key may contain embedded nulls.
void removeMember(const String& key);
/// Same as removeMember(const char* begin, const char* end, Value* removed),
/// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member.
*
* Update 'removed' iff removed.
* \param key may contain embedded nulls.
* \return true iff removed (no exceptions)
*/
bool removeMember(String const& key, Value* removed);
/// Same as removeMember(String const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element.
*
* O(n) expensive operations.
* Update 'removed' iff removed.
* \return true if removed (no exceptions)
*/
bool removeIndex(ArrayIndex index, Value* removed);
/// Return true if the object has a member named key.
/// \note 'key' must be null-terminated.
bool isMember(const char* key) const;
/// Return true if the object has a member named key.
/// \param key may contain embedded nulls.
bool isMember(const String& key) const;
/// Same as isMember(String const& key)const
bool isMember(const char* begin, const char* end) const;
/// \brief Return a list of the member names.
///
/// If null, return an empty list.
/// \pre type() is objectValue or nullValue
/// \post if type() was nullValue, it remains nullValue
Members getMemberNames() const;
/// \deprecated Always pass len.
JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
void setComment(const char* comment, CommentPlacement placement) {
setComment(String(comment, strlen(comment)), placement);
}
/// Comments must be //... or /* ... */
void setComment(const char* comment, size_t len, CommentPlacement placement) {
setComment(String(comment, len), placement);
}
/// Comments must be //... or /* ... */
void setComment(String comment, CommentPlacement placement);
bool hasComment(CommentPlacement placement) const;
/// Include delimiters and embedded newlines.
String getComment(CommentPlacement placement) const;
String toStyledString() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart(ptrdiff_t start);
void setOffsetLimit(ptrdiff_t limit);
ptrdiff_t getOffsetStart() const;
ptrdiff_t getOffsetLimit() const;
private:
void setType(ValueType v) {
bits_.value_type_ = static_cast<unsigned char>(v);
}
bool isAllocated() const { return bits_.allocated_; }
void setIsAllocated(bool v) { bits_.allocated_ = v; }
void initBasic(ValueType type, bool allocated = false);
void dupPayload(const Value& other);
void releasePayload();
void dupMeta(const Value& other);
Value& resolveReference(const char* key);
Value& resolveReference(const char* key, const char* end);
// struct MemberNamesTransform
//{
// typedef const char *result_type;
// const char *operator()( const CZString &name ) const
// {
// return name.c_str();
// }
//};
union ValueHolder {
LargestInt int_;
LargestUInt uint_;
double real_;
bool bool_;
char* string_; // if allocated_, ptr to { unsigned, char[] }.
ObjectValues* map_;
} value_;
struct {
// Really a ValueType, but types should agree for bitfield packing.
unsigned int value_type_ : 8;
// Unless allocated_, string_ must be null-terminated.
unsigned int allocated_ : 1;
} bits_;
class Comments {
public:
Comments() = default;
Comments(const Comments& that);
Comments(Comments&& that) noexcept;
Comments& operator=(const Comments& that);
Comments& operator=(Comments&& that) noexcept;
bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const;
void set(CommentPlacement slot, String comment);
private:
using Array = std::array<String, numberOfCommentPlacement>;
std::unique_ptr<Array> ptr_;
};
Comments comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
ptrdiff_t start_;
ptrdiff_t limit_;
};
template <> inline bool Value::as<bool>() const { return asBool(); }
template <> inline bool Value::is<bool>() const { return isBool(); }
template <> inline Int Value::as<Int>() const { return asInt(); }
template <> inline bool Value::is<Int>() const { return isInt(); }
template <> inline UInt Value::as<UInt>() const { return asUInt(); }
template <> inline bool Value::is<UInt>() const { return isUInt(); }
#if defined(JSON_HAS_INT64)
template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
template <> inline bool Value::is<Int64>() const { return isInt64(); }
template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
#endif
template <> inline double Value::as<double>() const { return asDouble(); }
template <> inline bool Value::is<double>() const { return isDouble(); }
template <> inline String Value::as<String>() const { return asString(); }
template <> inline bool Value::is<String>() const { return isString(); }
/// These `as` specializations are type conversions, and do not have a
/// corresponding `is`.
template <> inline float Value::as<float>() const { return asFloat(); }
template <> inline const char* Value::as<const char*>() const {
return asCString();
}
/** \brief Experimental and untested: represents an element of the "path" to
* access a node.
*/
class JSON_API PathArgument {
public:
friend class Path;
PathArgument();
PathArgument(ArrayIndex index);
PathArgument(const char* key);
PathArgument(String key);
private:
enum Kind { kindNone = 0, kindIndex, kindKey };
String key_;
ArrayIndex index_{};
Kind kind_{kindNone};
};
/** \brief Experimental and untested: represents a "path" to access a node.
*
* Syntax:
* - "." => root node
* - ".[n]" => elements at index 'n' of root node (an array value)
* - ".name" => member named 'name' of root node (an object value)
* - ".name1.name2.name3"
* - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter
* - ".[%]" => index is provided as parameter
*/
class JSON_API Path {
public:
Path(const String& path, const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(),
const PathArgument& a5 = PathArgument());
const Value& resolve(const Value& root) const;
Value resolve(const Value& root, const Value& defaultValue) const;
/// Creates the "path" to access the specified node and returns a reference on
/// the node.
Value& make(Value& root) const;
private:
using InArgs = std::vector<const PathArgument*>;
using Args = std::vector<PathArgument>;
void makePath(const String& path, const InArgs& in);
void addPathInArg(const String& path, const InArgs& in,
InArgs::const_iterator& itInArg, PathArgument::Kind kind);
static void invalidPath(const String& path, int location);
Args args_;
};
/** \brief base class for Value iterators.
*
*/
class JSON_API ValueIteratorBase {
public:
using iterator_category = std::bidirectional_iterator_tag;
using size_t = unsigned int;
using difference_type = int;
using SelfType = ValueIteratorBase;
bool operator==(const SelfType& other) const { return isEqual(other); }
bool operator!=(const SelfType& other) const { return !isEqual(other); }
difference_type operator-(const SelfType& other) const {
return other.computeDistance(*this);
}
/// Return either the index or the member name of the referenced value as a
/// Value.
Value key() const;
/// Return the index of the referenced Value, or -1 if it is not an
/// arrayValue.
UInt index() const;
/// Return the member name of the referenced Value, or "" if it is not an
/// objectValue.
/// \note Avoid `c_str()` on result, as embedded zeroes are possible.
String name() const;
/// Return the member name of the referenced Value. "" if it is not an
/// objectValue.
/// \deprecated This cannot be used for UTF-8 strings, since there can be
/// embedded nulls.
JSONCPP_DEPRECATED("Use `key = name();` instead.")
char const* memberName() const;
/// Return the member name of the referenced Value, or NULL if it is not an
/// objectValue.
/// \note Better version than memberName(). Allows embedded nulls.
char const* memberName(char const** end) const;
protected:
/*! Internal utility functions to assist with implementing
* other iterator functions. The const and non-const versions
* of the "deref" protected methods expose the protected
* current_ member variable in a way that can often be
* optimized away by the compiler.
*/
const Value& deref() const;
Value& deref();
void increment();
void decrement();
difference_type computeDistance(const SelfType& other) const;
bool isEqual(const SelfType& other) const;
void copy(const SelfType& other);
private:
Value::ObjectValues::iterator current_;
// Indicates that iterator is for a null value.
bool isNull_{true};
public:
// For some reason, BORLAND needs these at the end, rather
// than earlier. No idea why.
ValueIteratorBase();
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
};
/** \brief const iterator for object and array value.
*
*/
class JSON_API ValueConstIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = const Value;
// typedef unsigned int size_t;
// typedef int difference_type;
using reference = const Value&;
using pointer = const Value*;
using SelfType = ValueConstIterator;
ValueConstIterator();
ValueConstIterator(ValueIterator const& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const ValueIteratorBase& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
reference operator*() const { return deref(); }
pointer operator->() const { return &deref(); }
};
/** \brief Iterator for object and array value.
*/
class JSON_API ValueIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = Value;
using size_t = unsigned int;
using difference_type = int;
using reference = Value&;
using pointer = Value*;
using SelfType = ValueIterator;
ValueIterator();
explicit ValueIterator(const ValueConstIterator& other);
ValueIterator(const ValueIterator& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const SelfType& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
/*! The return value of non-const iterators can be
* changed, so the these functions are not const
* because the returned references/pointers can be used
* to change state of the base class.
*/
reference operator*() const { return const_cast<reference>(deref()); }
pointer operator->() const { return const_cast<pointer>(&deref()); }
};
inline void swap(Value& a, Value& b) { a.swap(b); }
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_H_INCLUDED

View File

@ -0,0 +1,28 @@
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
// Note: version must be updated in three places when doing a release. This
// annoying process ensures that amalgamate, CMake, and meson all report the
// correct version.
// 1. /meson.build
// 2. /include/json/version.h
// 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.5"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 5
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED

View File

@ -0,0 +1,369 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_WRITER_H_INCLUDED
#define JSON_WRITER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <ostream>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
namespace Json {
class Value;
/**
*
* Usage:
* \code
* using namespace Json;
* void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
* { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* }
* \endcode
*/
class JSON_API StreamWriter {
protected:
OStream* sout_; // not owned; will not delete
public:
StreamWriter();
virtual ~StreamWriter();
/** Write Value into document as configured in sub-class.
* Do not take ownership of sout, but maintain a reference during function.
* \pre sout != NULL
* \return zero on success (For now, we always return zero, so check the
* stream instead.) \throw std::exception possibly, depending on
* configuration
*/
virtual int write(Value const& root, OStream* sout) = 0;
/** \brief A simple abstract factory.
*/
class JSON_API Factory {
public:
virtual ~Factory();
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual StreamWriter* newStreamWriter() const = 0;
}; // Factory
}; // StreamWriter
/** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted.
*/
String JSON_API writeString(StreamWriter::Factory const& factory,
Value const& root);
/** \brief Build a StreamWriter implementation.
* Usage:
* \code
* using namespace Json;
* Value value = ...;
* StreamWriterBuilder builder;
* builder["commentStyle"] = "None";
* builder["indentation"] = " "; // or whatever you like
* std::unique_ptr<Json::StreamWriter> writer(
* builder.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* \endcode
*/
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* Available settings (case-sensitive):
* - "commentStyle": "None" or "All"
* - "indentation": "<anything>".
* - Setting this to an empty string also omits newline characters.
* - "enableYAMLCompatibility": false or true
* - slightly change the whitespace around colons
* - "dropNullPlaceholders": false or true
* - Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
* - "useSpecialFloats": false or true
* - If true, outputs non-finite floating point values in the following way:
* NaN values as "NaN", positive infinity as "Infinity", and negative
* infinity as "-Infinity".
* - "precision": int
* - Number of precision digits for formatting of real values.
* - "precisionType": "significant"(default) or "decimal"
* - Type of precision for formatting of real values.
* - "emitUTF8": false or true
* - If true, outputs raw UTF8 strings instead of escaping them.
* You can examine 'settings_` yourself
* to see the defaults. You can also write and read them just like any
* JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
StreamWriterBuilder();
~StreamWriterBuilder() override;
/**
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
StreamWriter* newStreamWriter() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
};
/** \brief Abstract class for writers.
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
*/
class JSON_API Writer {
public:
virtual ~Writer();
virtual String write(const Value& root) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
*without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human'
*consumption,
* but may be useful to support feature such as RPC where bandwidth is limited.
* \sa Reader, Value
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API FastWriter
: public Writer {
public:
FastWriter();
~FastWriter() override = default;
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
void omitEndingLineFeed();
public: // overridden from Writer
String write(const Value& root) override;
private:
void writeValue(const Value& value);
String document_;
bool yamlCompatibilityEnabled_{false};
bool dropNullPlaceholders_{false};
bool omitEndingLineFeed_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
*human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
*line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
*types,
* and all the values fit on one lines, then print the array on a single
*line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
*#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API
StyledWriter : public Writer {
public:
StyledWriter();
~StyledWriter() override = default;
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
String write(const Value& root) override;
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
String document_;
String indentString_;
unsigned int rightMargin_{74};
unsigned int indentSize_{3};
bool addChildValues_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
types,
* and all the values fit on one lines, then print the array on a single
line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSON_API
StyledStreamWriter {
public:
/**
* \param indentation Each level will be indented by this amount extra.
*/
StyledStreamWriter(String indentation = "\t");
~StyledStreamWriter() = default;
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not
* return a value.
*/
void write(OStream& out, const Value& root);
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
OStream* document_;
String indentString_;
unsigned int rightMargin_{74};
String indentation_;
bool addChildValues_ : 1;
bool indented_ : 1;
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#if defined(JSON_HAS_INT64)
String JSON_API valueToString(Int value);
String JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64)
String JSON_API valueToString(LargestInt value);
String JSON_API valueToString(LargestUInt value);
String JSON_API valueToString(
double value, unsigned int precision = Value::defaultRealPrecision,
PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value);
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
JSON_API OStream& operator<<(OStream&, const Value& root);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include <json/config.h>
#endif
// Also support old flag NO_LOCALE_SUPPORT
#ifdef NO_LOCALE_SUPPORT
#define JSONCPP_NO_LOCALE_SUPPORT
#endif
#ifndef JSONCPP_NO_LOCALE_SUPPORT
#include <clocale>
#endif
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
static inline char getDecimalPoint() {
#ifdef JSONCPP_NO_LOCALE_SUPPORT
return '\0';
#else
struct lconv* lc = localeconv();
return lc ? *(lc->decimal_point) : '\0';
#endif
}
/// Converts a unicode code-point to UTF-8.
static inline String codePointToUTF8(unsigned int cp) {
String result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
using UIntToStringBuffer = char[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned integer to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
for (; begin != end; ++begin) {
if (*begin == ',') {
*begin = '.';
}
}
return begin;
}
template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
char decimalPoint = getDecimalPoint();
if (decimalPoint == '\0' || decimalPoint == '.') {
return;
}
for (; begin != end; ++begin) {
if (*begin == '.') {
*begin = decimalPoint;
}
}
}
/**
* Return iterator that would be the new end of the range [begin,end), if we
* were to delete zeros in the end of string, but not the last zero before '.'.
*/
template <typename Iter>
Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
for (; begin != end; --end) {
if (*(end - 1) != '0') {
return end;
}
// Don't delete the last zero before the decimal point.
if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
if (precision) {
return end;
}
return end - 2;
}
}
return end;
}
} // namespace Json
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase() : current_() {}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() { return current_->second; }
const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; }
void ValueIteratorBase::decrement() { --current_; }
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if (isNull_ && other.isNull_) {
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12
// RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
String ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey)
return String();
return String(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = nullptr;
return nullptr;
}
*end = cname + (*current_).first.length();
return cname;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator() = default;
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator::ValueConstIterator(ValueIterator const& other)
: ValueIteratorBase(other) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator() = default;
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {
throwRuntimeError("ConstIterator to Iterator should never be allowed.");
}
ValueIterator::ValueIterator(const ValueIterator& other) = default;
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
} // namespace Json

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
# Copyright (c) 2020 The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0
!openxr_loader_for_android.pom

View File

@ -0,0 +1,319 @@
// Copyright (c) 2020-2022, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
#include "android_utilities.h"
#ifdef __ANDROID__
#include <wrap/android.net.h>
#include <wrap/android.content.h>
#include <wrap/android.database.h>
#include <json/value.h>
#include <openxr/openxr.h>
#include <sstream>
#include <vector>
#include <android/log.h>
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "openxr_loader", __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "openxr_loader", __VA_ARGS__)
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "openxr_loader", __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "openxr_loader", __VA_ARGS__)
namespace openxr_android {
using wrap::android::content::ContentUris;
using wrap::android::content::Context;
using wrap::android::database::Cursor;
using wrap::android::net::Uri;
using wrap::android::net::Uri_Builder;
// Code in here corresponds roughly to the Java "BrokerContract" class and subclasses.
namespace {
constexpr auto AUTHORITY = "org.khronos.openxr.runtime_broker";
constexpr auto SYSTEM_AUTHORITY = "org.khronos.openxr.system_runtime_broker";
constexpr auto BASE_PATH = "openxr";
constexpr auto ABI_PATH = "abi";
constexpr auto RUNTIMES_PATH = "runtimes";
constexpr const char *getBrokerAuthority(bool systemBroker) { return systemBroker ? SYSTEM_AUTHORITY : AUTHORITY; }
struct BaseColumns {
/**
* The unique ID for a row.
*/
[[maybe_unused]] static constexpr auto ID = "_id";
};
/**
* Contains details for the /openxr/[major_ver]/abi/[abi]/runtimes/active URI.
* <p>
* This URI represents a "table" containing at most one item, the currently active runtime. The
* policy of which runtime is chosen to be active (if more than one is installed) is left to the
* content provider.
* <p>
* No sort order is required to be honored by the content provider.
*/
namespace active_runtime {
/**
* Final path component to this URI.
*/
static constexpr auto TABLE_PATH = "active";
/**
* Create a content URI for querying the data on the active runtime for a
* given major version of OpenXR.
*
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
* @param majorVer The major version of OpenXR.
* @param abi The Android ABI name in use.
* @return A content URI for a single item: the active runtime.
*/
static Uri makeContentUri(bool systemBroker, int majorVersion, const char *abi) {
auto builder = Uri_Builder::construct();
builder.scheme("content")
.authority(getBrokerAuthority(systemBroker))
.appendPath(BASE_PATH)
.appendPath(std::to_string(majorVersion))
.appendPath(ABI_PATH)
.appendPath(abi)
.appendPath(RUNTIMES_PATH)
.appendPath(TABLE_PATH);
ContentUris::appendId(builder, 0);
return builder.build();
}
struct Columns : BaseColumns {
/**
* Constant for the PACKAGE_NAME column name
*/
static constexpr auto PACKAGE_NAME = "package_name";
/**
* Constant for the NATIVE_LIB_DIR column name
*/
static constexpr auto NATIVE_LIB_DIR = "native_lib_dir";
/**
* Constant for the SO_FILENAME column name
*/
static constexpr auto SO_FILENAME = "so_filename";
/**
* Constant for the HAS_FUNCTIONS column name.
* <p>
* If this column contains true, you should check the /functions/ URI for that runtime.
*/
static constexpr auto HAS_FUNCTIONS = "has_functions";
};
} // namespace active_runtime
/**
* Contains details for the /openxr/[major_ver]/abi/[abi]/runtimes/[package]/functions URI.
* <p>
* This URI is for package-specific function name remapping. Since this is an optional field in
* the corresponding JSON manifests for OpenXR, it is optional here as well. If the active
* runtime contains "true" in its "has_functions" column, then this table must exist and be
* queryable.
* <p>
* No sort order is required to be honored by the content provider.
*/
namespace functions {
/**
* Final path component to this URI.
*/
static constexpr auto TABLE_PATH = "functions";
/**
* Create a content URI for querying all rows of the function remapping data for a given
* runtime package and major version of OpenXR.
*
* @param systemBroker If the system runtime broker (instead of the installable one) should be queried.
* @param majorVer The major version of OpenXR.
* @param packageName The package name of the runtime.
* @param abi The Android ABI name in use.
* @return A content URI for the entire table: the function remapping for that runtime.
*/
static Uri makeContentUri(bool systemBroker, int majorVersion, std::string const &packageName, const char *abi) {
auto builder = Uri_Builder::construct();
builder.scheme("content")
.authority(getBrokerAuthority(systemBroker))
.appendPath(BASE_PATH)
.appendPath(std::to_string(majorVersion))
.appendPath(ABI_PATH)
.appendPath(abi)
.appendPath(RUNTIMES_PATH)
.appendPath(packageName)
.appendPath(TABLE_PATH);
return builder.build();
}
struct Columns : BaseColumns {
/**
* Constant for the FUNCTION_NAME column name
*/
static constexpr auto FUNCTION_NAME = "function_name";
/**
* Constant for the SYMBOL_NAME column name
*/
static constexpr auto SYMBOL_NAME = "symbol_name";
};
} // namespace functions
} // namespace
static inline jni::Array<std::string> makeArray(std::initializer_list<const char *> &&list) {
auto ret = jni::Array<std::string>{(long)list.size()};
long i = 0;
for (auto &&elt : list) {
ret.setElement(i, elt);
++i;
}
return ret;
}
static constexpr auto TAG = "OpenXR-Loader";
#if defined(__arm__)
static constexpr auto ABI = "armeabi-v7l";
#elif defined(__aarch64__)
static constexpr auto ABI = "arm64-v8a";
#elif defined(__i386__)
static constexpr auto ABI = "x86";
#elif defined(__x86_64__)
static constexpr auto ABI = "x86_64";
#else
#error "Unknown ABI!"
#endif
/// Helper class to generate the jsoncpp object corresponding to a synthetic runtime manifest.
class JsonManifestBuilder {
public:
JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath);
JsonManifestBuilder &function(const std::string &functionName, const std::string &symbolName);
Json::Value build() const { return root_node; }
private:
Json::Value root_node;
};
inline JsonManifestBuilder::JsonManifestBuilder(const std::string &libraryPathParent, const std::string &libraryPath)
: root_node(Json::objectValue) {
root_node["file_format_version"] = "1.0.0";
root_node["instance_extensions"] = Json::Value(Json::arrayValue);
root_node["functions"] = Json::Value(Json::objectValue);
root_node[libraryPathParent] = Json::objectValue;
root_node[libraryPathParent]["library_path"] = libraryPath;
}
inline JsonManifestBuilder &JsonManifestBuilder::function(const std::string &functionName, const std::string &symbolName) {
root_node["functions"][functionName] = symbolName;
return *this;
}
static constexpr const char *getBrokerTypeName(bool systemBroker) { return systemBroker ? "system" : "installable"; }
static int populateFunctions(wrap::android::content::Context const &context, bool systemBroker, const std::string &packageName,
JsonManifestBuilder &builder) {
jni::Array<std::string> projection = makeArray({functions::Columns::FUNCTION_NAME, functions::Columns::SYMBOL_NAME});
auto uri = functions::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), packageName, ABI);
ALOGI("populateFunctions: Querying URI: %s", uri.toString().c_str());
Cursor cursor = context.getContentResolver().query(uri, projection);
if (cursor.isNull()) {
ALOGE("Null cursor when querying content resolver for functions.");
return -1;
}
if (cursor.getCount() < 1) {
ALOGE("Non-null but empty cursor when querying content resolver for functions.");
cursor.close();
return -1;
}
auto functionIndex = cursor.getColumnIndex(functions::Columns::FUNCTION_NAME);
auto symbolIndex = cursor.getColumnIndex(functions::Columns::SYMBOL_NAME);
while (cursor.moveToNext()) {
builder.function(cursor.getString(functionIndex), cursor.getString(symbolIndex));
}
cursor.close();
return 0;
}
/// Get cursor for active runtime, parameterized by whether or not we use the system broker
static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection,
bool systemBroker, Cursor &cursor) {
auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI);
ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str());
try {
cursor = context.getContentResolver().query(uri, projection);
} catch (const std::exception &e) {
ALOGW("Exception when querying %s content resolver: %s", getBrokerTypeName(systemBroker), e.what());
cursor = {};
return false;
}
if (cursor.isNull()) {
ALOGW("Null cursor when querying %s content resolver.", getBrokerTypeName(systemBroker));
cursor = {};
return false;
}
if (cursor.getCount() < 1) {
ALOGW("Non-null but empty cursor when querying %s content resolver.", getBrokerTypeName(systemBroker));
cursor.close();
cursor = {};
return false;
}
return true;
}
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest) {
jni::Array<std::string> projection = makeArray({active_runtime::Columns::PACKAGE_NAME, active_runtime::Columns::NATIVE_LIB_DIR,
active_runtime::Columns::SO_FILENAME, active_runtime::Columns::HAS_FUNCTIONS});
// First, try getting the installable broker's provider
bool systemBroker = false;
Cursor cursor;
if (!getActiveRuntimeCursor(context, projection, systemBroker, cursor)) {
// OK, try the system broker as a fallback.
systemBroker = true;
getActiveRuntimeCursor(context, projection, systemBroker, cursor);
}
if (cursor.isNull()) {
// Couldn't find either broker
ALOGE("Could access neither the installable nor system runtime broker.");
return -1;
}
cursor.moveToFirst();
auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME));
auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME));
auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1;
__android_log_print(ANDROID_LOG_INFO, TAG, "Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s",
packageName.c_str(), libDir.c_str(), filename.c_str(), (hasFunctions ? "yes" : "no"));
auto lib_path = libDir + "/" + filename;
cursor.close();
JsonManifestBuilder builder{"runtime", lib_path};
if (hasFunctions) {
int result = populateFunctions(context, systemBroker, packageName, builder);
if (result != 0) {
return result;
}
}
virtualManifest = builder.build();
return 0;
}
} // namespace openxr_android
#endif // __ANDROID__

View File

@ -0,0 +1,32 @@
// Copyright (c) 2020-2022, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
#pragma once
#ifdef __ANDROID__
#include "wrap/android.content.h"
#include <string>
namespace Json {
class Value;
} // namespace Json
namespace openxr_android {
using wrap::android::content::Context;
/*!
* Find the single active OpenXR runtime on the system, and return a constructed JSON object representing it.
*
* @param context An Android context, preferably an Activity Context.
* @param[out] virtualManifest The Json::Value to fill with the virtual manifest.
*
* @return 0 on success, something else on failure.
*/
int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &context, Json::Value &virtualManifest);
} // namespace openxr_android
#endif // __ANDROID__

View File

@ -0,0 +1,399 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#include "api_layer_interface.hpp"
#include "loader_interfaces.h"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "manifest_file.hpp"
#include "platform_utils.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#define OPENXR_ENABLE_LAYERS_ENV_VAR "XR_ENABLE_API_LAYERS"
// Add any layers defined in the loader layer environment variable.
static void AddEnvironmentApiLayers(std::vector<std::string>& enabled_layers) {
std::string layers = PlatformUtilsGetEnv(OPENXR_ENABLE_LAYERS_ENV_VAR);
std::size_t last_found = 0;
std::size_t found = layers.find_first_of(PATH_SEPARATOR);
std::string cur_search;
// Handle any path listings in the string (separated by the appropriate path separator)
while (found != std::string::npos) {
cur_search = layers.substr(last_found, found - last_found);
enabled_layers.push_back(cur_search);
last_found = found + 1;
found = layers.find_first_of(PATH_SEPARATOR, last_found);
}
// If there's something remaining in the string, copy it over
if (last_found < layers.size()) {
cur_search = layers.substr(last_found);
enabled_layers.push_back(cur_search);
}
}
XrResult ApiLayerInterface::GetApiLayerProperties(const std::string& openxr_command, uint32_t incoming_count,
uint32_t* outgoing_count, XrApiLayerProperties* api_layer_properties) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files;
uint32_t manifest_count = 0;
// Validate props struct before proceeding
if (0 < incoming_count && nullptr != api_layer_properties) {
for (uint32_t i = 0; i < incoming_count; i++) {
if (XR_TYPE_API_LAYER_PROPERTIES != api_layer_properties[i].type) {
LoaderLogger::LogErrorMessage(openxr_command,
"VUID-XrApiLayerProperties-type-type: unknown type in api_layer_properties");
return XR_ERROR_VALIDATION_FAILURE;
}
}
}
// "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer,
// and the function sets elementCountOutput." - 2.11
if (nullptr == outgoing_count) {
return XR_ERROR_VALIDATION_FAILURE;
}
// Find any implicit layers which we may need to report information for.
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any explicit layers which we may need to report information for.
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files);
}
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage(openxr_command,
"ApiLayerInterface::GetApiLayerProperties - failed searching for API layer manifest files");
return result;
}
manifest_count = static_cast<uint32_t>(manifest_files.size());
if (nullptr == outgoing_count) {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-propertyCountOutput-parameter: null propertyCountOutput");
return XR_ERROR_VALIDATION_FAILURE;
}
*outgoing_count = manifest_count;
if (0 == incoming_count) {
// capacity check only
return XR_SUCCESS;
}
if (nullptr == api_layer_properties) {
// incoming_count is not 0 BUT the api_layer_properties is NULL
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-properties-parameter: non-zero capacity but null array");
return XR_ERROR_VALIDATION_FAILURE;
}
if (incoming_count < manifest_count) {
LoaderLogger::LogErrorMessage(
"xrEnumerateInstanceExtensionProperties",
"VUID-xrEnumerateApiLayerProperties-propertyCapacityInput-parameter: insufficient space in array");
return XR_ERROR_SIZE_INSUFFICIENT;
}
for (uint32_t prop = 0; prop < incoming_count && prop < manifest_count; ++prop) {
manifest_files[prop]->PopulateApiLayerProperties(api_layer_properties[prop]);
}
return XR_SUCCESS;
}
XrResult ApiLayerInterface::GetInstanceExtensionProperties(const std::string& openxr_command, const char* layer_name,
std::vector<XrExtensionProperties>& extension_properties) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> manifest_files;
// If a layer name is supplied, only use the information out of that one layer
if (nullptr != layer_name && 0 != strlen(layer_name)) {
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any explicit layers which we may need to report information for.
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, manifest_files);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage(
openxr_command,
"ApiLayerInterface::GetInstanceExtensionProperties - failed searching for API layer manifest files");
return result;
}
bool found = false;
auto num_files = static_cast<uint32_t>(manifest_files.size());
for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
// If a layer with the provided name exists, get it's instance extension information.
if (manifest_files[man_file]->LayerName() == layer_name) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
found = true;
break;
}
}
// If nothing found, report 0
if (!found) {
return XR_ERROR_API_LAYER_NOT_PRESENT;
}
}
// Otherwise, we want to add only implicit API layers and explicit API layers enabled using the environment variables
} else {
XrResult result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, manifest_files);
if (XR_SUCCEEDED(result)) {
// Find any environmentally enabled explicit layers. If they're present, treat them like implicit layers
// since we know that they're going to be enabled.
std::vector<std::string> env_enabled_layers;
AddEnvironmentApiLayers(env_enabled_layers);
if (!env_enabled_layers.empty()) {
std::vector<std::unique_ptr<ApiLayerManifestFile>> exp_layer_man_files = {};
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, exp_layer_man_files);
if (XR_SUCCEEDED(result)) {
for (auto& exp_layer_man_file : exp_layer_man_files) {
for (std::string& enabled_layer : env_enabled_layers) {
// If this is an enabled layer, transfer it over to the manifest list.
if (enabled_layer == exp_layer_man_file->LayerName()) {
manifest_files.push_back(std::move(exp_layer_man_file));
break;
}
}
}
}
}
}
// Grab the layer instance extensions information
auto num_files = static_cast<uint32_t>(manifest_files.size());
for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
}
}
return XR_SUCCESS;
}
XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uint32_t enabled_api_layer_count,
const char* const* enabled_api_layer_names,
std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces) {
XrResult last_error = XR_SUCCESS;
std::unordered_set<std::string> layers_already_found;
bool any_loaded = false;
std::vector<std::unique_ptr<ApiLayerManifestFile>> enabled_layer_manifest_files_in_init_order = {};
// Find any implicit layers.
XrResult result =
ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_IMPLICIT_API_LAYER, enabled_layer_manifest_files_in_init_order);
for (const auto& enabled_layer_manifest_file : enabled_layer_manifest_files_in_init_order) {
layers_already_found.insert(enabled_layer_manifest_file->LayerName());
}
// Find any explicit layers.
std::vector<std::unique_ptr<ApiLayerManifestFile>> explicit_layer_manifest_files = {};
if (XR_SUCCEEDED(result)) {
result = ApiLayerManifestFile::FindManifestFiles(MANIFEST_TYPE_EXPLICIT_API_LAYER, explicit_layer_manifest_files);
}
bool found_all_layers = true;
if (XR_SUCCEEDED(result)) {
// Put all explicit and then xrCreateInstance enabled layers into a string vector
std::vector<std::string> enabled_explicit_api_layer_names = {};
AddEnvironmentApiLayers(enabled_explicit_api_layer_names);
if (enabled_api_layer_count > 0) {
if (nullptr == enabled_api_layer_names) {
LoaderLogger::LogErrorMessage(
"xrCreateInstance",
"VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter: enabledApiLayerCount is non-0 but array is NULL");
LoaderLogger::LogErrorMessage(
"xrCreateInstance", "VUID-xrCreateInstance-info-parameter: something wrong with XrInstanceCreateInfo contents");
return XR_ERROR_VALIDATION_FAILURE;
}
std::copy(enabled_api_layer_names, enabled_api_layer_names + enabled_api_layer_count,
std::back_inserter(enabled_explicit_api_layer_names));
}
// add explicit layers to list of layers to enable
for (const auto& layer_name : enabled_explicit_api_layer_names) {
bool found_this_layer = false;
for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) {
bool erased_layer_manifest_file = false;
if (layers_already_found.count(layer_name) > 0) {
found_this_layer = true;
} else if (layer_name == (*it)->LayerName()) {
found_this_layer = true;
layers_already_found.insert(layer_name);
enabled_layer_manifest_files_in_init_order.push_back(std::move(*it));
it = explicit_layer_manifest_files.erase(it);
erased_layer_manifest_file = true;
}
if (!erased_layer_manifest_file) {
it++;
}
}
// If even one of the layers wasn't found, we want to return an error
if (!found_this_layer) {
found_all_layers = false;
std::string error_message = "ApiLayerInterface::LoadApiLayers - failed to find layer ";
error_message += layer_name;
LoaderLogger::LogErrorMessage(openxr_command, error_message);
}
}
}
for (std::unique_ptr<ApiLayerManifestFile>& manifest_file : enabled_layer_manifest_files_in_init_order) {
LoaderPlatformLibraryHandle layer_library = LoaderPlatformLibraryOpen(manifest_file->LibraryPath());
if (nullptr == layer_library) {
if (!any_loaded) {
last_error = XR_ERROR_FILE_ACCESS_ERROR;
}
std::string library_message = LoaderPlatformLibraryOpenError(manifest_file->LibraryPath());
std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer ";
warning_message += manifest_file->LayerName();
warning_message += ", failed to load with message \"";
warning_message += library_message;
warning_message += "\"";
LoaderLogger::LogWarningMessage(openxr_command, warning_message);
continue;
}
// Get and settle on an layer interface version (using any provided name if required).
std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderApiLayerInterface");
auto negotiate = reinterpret_cast<PFN_xrNegotiateLoaderApiLayerInterface>(
LoaderPlatformLibraryGetProcAddr(layer_library, function_name));
if (nullptr == negotiate) {
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName()
<< " because negotiation function " << function_name << " was not found";
LoaderLogger::LogErrorMessage(openxr_command, oss.str());
LoaderPlatformLibraryClose(layer_library);
last_error = XR_ERROR_API_LAYER_NOT_PRESENT;
continue;
}
// Loader info for negotiation
XrNegotiateLoaderInfo loader_info = {};
loader_info.structType = XR_LOADER_INTERFACE_STRUCT_LOADER_INFO;
loader_info.structVersion = XR_LOADER_INFO_STRUCT_VERSION;
loader_info.structSize = sizeof(XrNegotiateLoaderInfo);
loader_info.minInterfaceVersion = 1;
loader_info.maxInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION;
loader_info.minApiVersion = XR_MAKE_VERSION(1, 0, 0);
loader_info.maxApiVersion = XR_MAKE_VERSION(1, 0x3ff, 0xfff); // Maximum allowed version for this major version.
// Set up the layer return structure
XrNegotiateApiLayerRequest api_layer_info = {};
api_layer_info.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST;
api_layer_info.structVersion = XR_API_LAYER_INFO_STRUCT_VERSION;
api_layer_info.structSize = sizeof(XrNegotiateApiLayerRequest);
XrResult res = negotiate(&loader_info, manifest_file->LayerName().c_str(), &api_layer_info);
// If we supposedly succeeded, but got a nullptr for getInstanceProcAddr
// then something still went wrong, so return with an error.
if (XR_SUCCEEDED(res) && nullptr == api_layer_info.getInstanceProcAddr) {
std::string warning_message = "ApiLayerInterface::LoadApiLayers skipping layer ";
warning_message += manifest_file->LayerName();
warning_message += ", negotiation did not return a valid getInstanceProcAddr";
LoaderLogger::LogWarningMessage(openxr_command, warning_message);
res = XR_ERROR_FILE_CONTENTS_INVALID;
}
if (XR_FAILED(res)) {
if (!any_loaded) {
last_error = res;
}
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers skipping layer " << manifest_file->LayerName()
<< " due to failed negotiation with error " << res;
LoaderLogger::LogWarningMessage(openxr_command, oss.str());
LoaderPlatformLibraryClose(layer_library);
continue;
}
{
std::ostringstream oss;
oss << "ApiLayerInterface::LoadApiLayers succeeded loading layer " << manifest_file->LayerName()
<< " using interface version " << api_layer_info.layerInterfaceVersion << " and OpenXR API version "
<< XR_VERSION_MAJOR(api_layer_info.layerApiVersion) << "." << XR_VERSION_MINOR(api_layer_info.layerApiVersion);
LoaderLogger::LogInfoMessage(openxr_command, oss.str());
}
// Grab the list of extensions this layer supports for easy filtering after the
// xrCreateInstance call
std::vector<std::string> supported_extensions;
std::vector<XrExtensionProperties> extension_properties;
manifest_file->GetInstanceExtensionProperties(extension_properties);
supported_extensions.reserve(extension_properties.size());
for (XrExtensionProperties& ext_prop : extension_properties) {
supported_extensions.emplace_back(ext_prop.extensionName);
}
// Add this API layer to the vector
api_layer_interfaces.emplace_back(new ApiLayerInterface(manifest_file->LayerName(), layer_library, supported_extensions,
api_layer_info.getInstanceProcAddr,
api_layer_info.createApiLayerInstance));
// If we load one, clear all errors.
any_loaded = true;
last_error = XR_SUCCESS;
}
// Set error here to preserve prior error behavior
if (!found_all_layers) {
last_error = XR_ERROR_API_LAYER_NOT_PRESENT;
}
// If we failed catastrophically for some reason, clean up everything.
if (XR_FAILED(last_error)) {
api_layer_interfaces.clear();
}
return last_error;
}
ApiLayerInterface::ApiLayerInterface(const std::string& layer_name, LoaderPlatformLibraryHandle layer_library,
std::vector<std::string>& supported_extensions,
PFN_xrGetInstanceProcAddr get_instance_proc_addr,
PFN_xrCreateApiLayerInstance create_api_layer_instance)
: _layer_name(layer_name),
_layer_library(layer_library),
_get_instance_proc_addr(get_instance_proc_addr),
_create_api_layer_instance(create_api_layer_instance),
_supported_extensions(supported_extensions) {}
ApiLayerInterface::~ApiLayerInterface() {
std::string info_message = "ApiLayerInterface being destroyed for layer ";
info_message += _layer_name;
LoaderLogger::LogInfoMessage("", info_message);
LoaderPlatformLibraryClose(_layer_library);
}
bool ApiLayerInterface::SupportsExtension(const std::string& extension_name) const {
bool found_prop = false;
for (const std::string& supported_extension : _supported_extensions) {
if (supported_extension == extension_name) {
found_prop = true;
break;
}
}
return found_prop;
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <openxr/openxr.h>
#include "loader_platform.hpp"
#include "loader_interfaces.h"
struct XrGeneratedDispatchTable;
class ApiLayerInterface {
public:
// Factory method
static XrResult LoadApiLayers(const std::string& openxr_command, uint32_t enabled_api_layer_count,
const char* const* enabled_api_layer_names,
std::vector<std::unique_ptr<ApiLayerInterface>>& api_layer_interfaces);
// Static queries
static XrResult GetApiLayerProperties(const std::string& openxr_command, uint32_t incoming_count, uint32_t* outgoing_count,
XrApiLayerProperties* api_layer_properties);
static XrResult GetInstanceExtensionProperties(const std::string& openxr_command, const char* layer_name,
std::vector<XrExtensionProperties>& extension_properties);
ApiLayerInterface(const std::string& layer_name, LoaderPlatformLibraryHandle layer_library,
std::vector<std::string>& supported_extensions, PFN_xrGetInstanceProcAddr get_instance_proc_addr,
PFN_xrCreateApiLayerInstance create_api_layer_instance);
virtual ~ApiLayerInterface();
PFN_xrGetInstanceProcAddr GetInstanceProcAddrFuncPointer() { return _get_instance_proc_addr; }
PFN_xrCreateApiLayerInstance GetCreateApiLayerInstanceFuncPointer() { return _create_api_layer_instance; }
std::string LayerName() { return _layer_name; }
// Generated methods
bool SupportsExtension(const std::string& extension_name) const;
private:
std::string _layer_name;
LoaderPlatformLibraryHandle _layer_library;
PFN_xrGetInstanceProcAddr _get_instance_proc_addr;
PFN_xrCreateApiLayerInstance _create_api_layer_instance;
std::vector<std::string> _supported_extensions;
};

View File

@ -0,0 +1,40 @@
// Copyright (c) 2019-2022, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com>
//
// Provides protection for C ABI functions if standard library functions may throw.
#pragma once
#ifdef OPENXR_HAVE_COMMON_CONFIG
#include "common_config.h"
#endif // OPENXR_HAVE_COMMON_CONFIG
#ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
#define XRLOADER_ABI_TRY
#define XRLOADER_ABI_CATCH_BAD_ALLOC_OOM
#define XRLOADER_ABI_CATCH_FALLBACK
#else
#include <stdexcept>
#define XRLOADER_ABI_TRY try
#define XRLOADER_ABI_CATCH_BAD_ALLOC_OOM \
catch (const std::bad_alloc&) { \
LoaderLogger::LogErrorMessage("", "failed allocating memory"); \
return XR_ERROR_OUT_OF_MEMORY; \
}
#define XRLOADER_ABI_CATCH_FALLBACK \
catch (const std::exception& e) { \
LoaderLogger::LogErrorMessage("", "Unknown failure: " + std::string(e.what())); \
return XR_ERROR_RUNTIME_FAILURE; \
} \
catch (...) { \
LoaderLogger::LogErrorMessage("", "Unknown failure"); \
return XR_ERROR_RUNTIME_FAILURE; \
}
#endif

View File

@ -0,0 +1,847 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com>
//
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#include "api_layer_interface.hpp"
#include "exception_handling.hpp"
#include "hex_and_handles.h"
#include "loader_instance.hpp"
#include "loader_logger_recorders.hpp"
#include "loader_logger.hpp"
#include "loader_platform.hpp"
#include "runtime_interface.hpp"
#include "xr_generated_dispatch_table.h"
#include "xr_generated_loader.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
// Global loader lock to:
// 1. Ensure ActiveLoaderInstance get and set operations are done atomically.
// 2. Ensure RuntimeInterface isn't used to unload the runtime while the runtime is in use.
std::mutex &GetGlobalLoaderMutex() {
static std::mutex loader_mutex;
return loader_mutex;
}
// Prototypes for the debug utils calls used internally.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineCreateDebugUtilsMessengerEXT(
XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo, XrDebugUtilsMessengerEXT *messenger);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger);
// Terminal functions needed by xrCreateInstance.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance, const char *, PFN_xrVoidFunction *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *, XrInstance *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *,
const struct XrApiLayerCreateInfo *, XrInstance *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance, const XrDebugUtilsObjectNameInfoEXT *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance,
const XrDebugUtilsMessengerCreateInfoEXT *,
XrDebugUtilsMessengerEXT *);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT);
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData);
// Utility template function meant to validate if a fixed size string contains
// a null-terminator.
template <size_t max_length>
inline bool IsMissingNullTerminator(const char (&str)[max_length]) {
for (size_t index = 0; index < max_length; ++index) {
if (str[index] == '\0') {
return false;
}
}
return true;
}
// ---- Core 1.0 manual loader trampoline functions
#ifdef XR_KHR_LOADER_INIT_SUPPORT // platforms that support XR_KHR_loader_init.
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrInitializeLoaderKHR", "Entering loader trampoline");
return InitializeLoader(loaderInitInfo);
}
XRLOADER_ABI_CATCH_FALLBACK
#endif
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrApiLayerProperties *properties) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrEnumerateApiLayerProperties", "Entering loader trampoline");
// Make sure only one thread is attempting to read the JSON files at a time.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
XrResult result = ApiLayerInterface::GetApiLayerProperties("xrEnumerateApiLayerProperties", propertyCapacityInput,
propertyCountOutput, properties);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrEnumerateApiLayerProperties", "Failed ApiLayerInterface::GetApiLayerProperties");
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrEnumerateInstanceExtensionProperties(const char *layerName, uint32_t propertyCapacityInput, uint32_t *propertyCountOutput,
XrExtensionProperties *properties) XRLOADER_ABI_TRY {
bool just_layer_properties = false;
LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Entering loader trampoline");
// "Independent of elementCapacityInput or elements parameters, elementCountOutput must be a valid pointer,
// and the function sets elementCountOutput." - 2.11
if (nullptr == propertyCountOutput) {
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr != layerName && 0 != strlen(layerName)) {
// Application is only interested in layer's properties, not all of them.
just_layer_properties = true;
}
std::vector<XrExtensionProperties> extension_properties = {};
XrResult result;
{
// Make sure the runtime isn't unloaded while this call is in progress.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
// Get the layer extension properties
result = ApiLayerInterface::GetInstanceExtensionProperties("xrEnumerateInstanceExtensionProperties", layerName,
extension_properties);
if (XR_SUCCEEDED(result) && !just_layer_properties) {
// If not specific to a layer, get the runtime extension properties
result = RuntimeInterface::LoadRuntime("xrEnumerateInstanceExtensionProperties");
if (XR_SUCCEEDED(result)) {
RuntimeInterface::GetRuntime().GetInstanceExtensionProperties(extension_properties);
} else {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
"Failed to find default runtime with RuntimeInterface::LoadRuntime()");
}
}
}
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties", "Failed querying extension properties");
return result;
}
// If this is not in reference to a specific layer, then add the loader-specific extension properties as well.
// These are extensions that the loader directly supports.
if (!just_layer_properties) {
for (const XrExtensionProperties &loader_prop : LoaderInstance::LoaderSpecificExtensions()) {
bool found_prop = false;
for (XrExtensionProperties &existing_prop : extension_properties) {
if (0 == strcmp(existing_prop.extensionName, loader_prop.extensionName)) {
found_prop = true;
// Use the loader version if it is newer
if (existing_prop.extensionVersion < loader_prop.extensionVersion) {
existing_prop.extensionVersion = loader_prop.extensionVersion;
}
break;
}
}
// Only add extensions not supported by the loader
if (!found_prop) {
extension_properties.push_back(loader_prop);
}
}
}
auto num_extension_properties = static_cast<uint32_t>(extension_properties.size());
if (propertyCapacityInput == 0) {
*propertyCountOutput = num_extension_properties;
} else if (nullptr != properties) {
if (propertyCapacityInput < num_extension_properties) {
*propertyCountOutput = num_extension_properties;
LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-propertyCountOutput-parameter",
"xrEnumerateInstanceExtensionProperties", "insufficient space in array");
return XR_ERROR_SIZE_INSUFFICIENT;
}
uint32_t num_to_copy = num_extension_properties;
// Determine how many extension properties we can copy over
if (propertyCapacityInput < num_to_copy) {
num_to_copy = propertyCapacityInput;
}
bool properties_valid = true;
for (uint32_t prop = 0; prop < propertyCapacityInput && prop < extension_properties.size(); ++prop) {
if (XR_TYPE_EXTENSION_PROPERTIES != properties[prop].type) {
properties_valid = false;
LoaderLogger::LogValidationErrorMessage("VUID-XrExtensionProperties-type-type",
"xrEnumerateInstanceExtensionProperties", "unknown type in properties");
}
if (properties_valid) {
properties[prop] = extension_properties[prop];
}
}
if (!properties_valid) {
LoaderLogger::LogValidationErrorMessage("VUID-xrEnumerateInstanceExtensionProperties-properties-parameter",
"xrEnumerateInstanceExtensionProperties", "invalid properties");
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr != propertyCountOutput) {
*propertyCountOutput = num_to_copy;
}
} else {
// incoming_count is not 0 BUT the properties is NULL
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderLogger::LogVerboseMessage("xrEnumerateInstanceExtensionProperties", "Completed loader trampoline");
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrCreateInstance(const XrInstanceCreateInfo *info,
XrInstance *instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader trampoline");
if (nullptr == info) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance", "must be non-NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
// If application requested OpenXR API version is higher than the loader version, then we need to throw
// an error.
uint16_t app_major = XR_VERSION_MAJOR(info->applicationInfo.apiVersion); // NOLINT
uint16_t app_minor = XR_VERSION_MINOR(info->applicationInfo.apiVersion); // NOLINT
uint16_t loader_major = XR_VERSION_MAJOR(XR_CURRENT_API_VERSION); // NOLINT
uint16_t loader_minor = XR_VERSION_MINOR(XR_CURRENT_API_VERSION); // NOLINT
if (app_major > loader_major || (app_major == loader_major && app_minor > loader_minor)) {
std::ostringstream oss;
oss << "xrCreateInstance called with invalid API version " << app_major << "." << app_minor
<< ". Max supported version is " << loader_major << "." << loader_minor;
LoaderLogger::LogErrorMessage("xrCreateInstance", oss.str());
return XR_ERROR_API_VERSION_UNSUPPORTED;
}
if (nullptr == instance) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-instance-parameter", "xrCreateInstance", "must be non-NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
// Make sure the ActiveLoaderInstance::IsAvailable check is done atomically with RuntimeInterface::LoadRuntime.
std::unique_lock<std::mutex> instance_lock(GetGlobalLoaderMutex());
// Check if there is already an XrInstance that is alive. If so, another instance cannot be created.
// The loader does not support multiple simultaneous instances because the loader is intended to be
// usable by apps using future OpenXR APIs (through xrGetInstanceProcAddr). Because the loader would
// not be aware of new handle types, it would not be able to look up the appropriate dispatch table
// in some cases.
if (ActiveLoaderInstance::IsAvailable()) { // If there is an XrInstance already alive.
LoaderLogger::LogErrorMessage("xrCreateInstance", "Loader does not support simultaneous XrInstances");
return XR_ERROR_LIMIT_REACHED;
}
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces;
XrResult result;
// Make sure only one thread is attempting to read the JSON files and use the instance.
{
// Load the available runtime
result = RuntimeInterface::LoadRuntime("xrCreateInstance");
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading runtime information");
} else {
// Load the appropriate layers
result = ApiLayerInterface::LoadApiLayers("xrCreateInstance", info->enabledApiLayerCount, info->enabledApiLayerNames,
api_layer_interfaces);
if (XR_FAILED(result)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "Failed loading layer information");
}
}
}
// Create the loader instance (only send down first runtime interface)
LoaderInstance *loader_instance = nullptr;
if (XR_SUCCEEDED(result)) {
std::unique_ptr<LoaderInstance> owned_loader_instance;
result = LoaderInstance::CreateInstance(LoaderXrTermGetInstanceProcAddr, LoaderXrTermCreateInstance,
LoaderXrTermCreateApiLayerInstance, std::move(api_layer_interfaces), info,
&owned_loader_instance);
if (XR_SUCCEEDED(result)) {
loader_instance = owned_loader_instance.get();
result = ActiveLoaderInstance::Set(std::move(owned_loader_instance), "xrCreateInstance");
}
}
if (XR_SUCCEEDED(result)) {
// Create a debug utils messenger if the create structure is in the "next" chain
const auto *next_header = reinterpret_cast<const XrBaseInStructure *>(info->next);
const XrDebugUtilsMessengerCreateInfoEXT *dbg_utils_create_info = nullptr;
while (next_header != nullptr) {
if (next_header->type == XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) {
LoaderLogger::LogInfoMessage("xrCreateInstance", "Found XrDebugUtilsMessengerCreateInfoEXT in \'next\' chain.");
dbg_utils_create_info = reinterpret_cast<const XrDebugUtilsMessengerCreateInfoEXT *>(next_header);
XrDebugUtilsMessengerEXT messenger;
result = LoaderTrampolineCreateDebugUtilsMessengerEXT(loader_instance->GetInstanceHandle(), dbg_utils_create_info,
&messenger);
if (XR_FAILED(result)) {
return XR_ERROR_VALIDATION_FAILURE;
}
loader_instance->SetDefaultDebugUtilsMessenger(messenger);
break;
}
next_header = reinterpret_cast<const XrBaseInStructure *>(next_header->next);
}
}
if (XR_FAILED(result)) {
// Ensure the global loader instance and runtime are destroyed if something went wrong.
ActiveLoaderInstance::Remove();
RuntimeInterface::UnloadRuntime("xrCreateInstance");
LoaderLogger::LogErrorMessage("xrCreateInstance", "xrCreateInstance failed");
} else {
*instance = loader_instance->GetInstanceHandle();
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader trampoline");
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader trampoline");
// Runtimes may detect XR_NULL_HANDLE provided as a required handle parameter and return XR_ERROR_HANDLE_INVALID. - 2.9
if (XR_NULL_HANDLE == instance) {
LoaderLogger::LogErrorMessage("xrDestroyInstance", "Instance handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
// Make sure the runtime isn't unloaded while it is being used by xrEnumerateInstanceExtensionProperties.
std::unique_lock<std::mutex> loader_lock(GetGlobalLoaderMutex());
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyInstance");
if (XR_FAILED(result)) {
return result;
}
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
// If we allocated a default debug utils messenger, free it
XrDebugUtilsMessengerEXT messenger = loader_instance->DefaultDebugUtilsMessenger();
if (messenger != XR_NULL_HANDLE) {
LoaderTrampolineDestroyDebugUtilsMessengerEXT(messenger);
}
// Now destroy the instance
if (XR_FAILED(dispatch_table->DestroyInstance(instance))) {
LoaderLogger::LogErrorMessage("xrDestroyInstance", "Unknown error occurred calling down chain");
}
// Get rid of the loader instance. This will make it possible to create another instance in the future.
ActiveLoaderInstance::Remove();
// Lock the instance create/destroy mutex
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader trampoline");
// Finally, unload the runtime if necessary
RuntimeInterface::UnloadRuntime("xrDestroyInstance");
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Core 1.0 manual loader terminator functions
// Validate that the applicationInfo structure in the XrInstanceCreateInfo is valid.
static XrResult ValidateApplicationInfo(const XrApplicationInfo &info) {
if (IsMissingNullTerminator<XR_MAX_APPLICATION_NAME_SIZE>(info.applicationName)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-applicationName-parameter", "xrCreateInstance",
"application name missing NULL terminator.");
return XR_ERROR_NAME_INVALID;
}
if (IsMissingNullTerminator<XR_MAX_ENGINE_NAME_SIZE>(info.engineName)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrApplicationInfo-engineName-parameter", "xrCreateInstance",
"engine name missing NULL terminator.");
return XR_ERROR_NAME_INVALID;
}
if (strlen(info.applicationName) == 0) {
LoaderLogger::LogErrorMessage("xrCreateInstance",
"VUID-XrApplicationInfo-engineName-parameter: application name can not be empty.");
return XR_ERROR_NAME_INVALID;
}
return XR_SUCCESS;
}
// Validate that the XrInstanceCreateInfo is valid
static XrResult ValidateInstanceCreateInfo(const XrInstanceCreateInfo *info) {
// Should have a valid 'type'
if (XR_TYPE_INSTANCE_CREATE_INFO != info->type) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-type-type", "xrCreateInstance",
"expected XR_TYPE_INSTANCE_CREATE_INFO.");
return XR_ERROR_VALIDATION_FAILURE;
}
// Flags must be 0
if (0 != info->createFlags) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-createFlags-zerobitmask", "xrCreateInstance",
"flags must be 0.");
return XR_ERROR_VALIDATION_FAILURE;
}
// ApplicationInfo struct must be valid
XrResult result = ValidateApplicationInfo(info->applicationInfo);
if (XR_FAILED(result)) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-applicationInfo-parameter", "xrCreateInstance",
"info->applicationInfo is not valid.");
return result;
}
// VUID-XrInstanceCreateInfo-enabledApiLayerNames-parameter already tested in LoadApiLayers()
if ((info->enabledExtensionCount != 0u) && nullptr == info->enabledExtensionNames) {
LoaderLogger::LogValidationErrorMessage("VUID-XrInstanceCreateInfo-enabledExtensionNames-parameter", "xrCreateInstance",
"enabledExtensionCount is non-0 but array is NULL");
return XR_ERROR_VALIDATION_FAILURE;
}
return XR_SUCCESS;
}
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateInstance(const XrInstanceCreateInfo *createInfo,
XrInstance *instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering loader terminator");
XrResult result = ValidateInstanceCreateInfo(createInfo);
if (XR_FAILED(result)) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateInstance-info-parameter", "xrCreateInstance",
"something wrong with XrInstanceCreateInfo contents");
return result;
}
result = RuntimeInterface::GetRuntime().CreateInstance(createInfo, instance);
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateApiLayerInstance(const XrInstanceCreateInfo *info,
const struct XrApiLayerCreateInfo * /*apiLayerInfo*/,
XrInstance *instance) {
return LoaderXrTermCreateInstance(info, instance);
}
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyInstance(XrInstance instance) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Entering loader terminator");
LoaderLogger::GetInstance().RemoveLogRecordersForXrInstance(instance);
XrResult result = RuntimeInterface::GetRuntime().DestroyInstance(instance);
LoaderLogger::LogVerboseMessage("xrDestroyInstance", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
// A few instance commands need to go through a loader terminator.
// Otherwise, go directly to the runtime version of the command if it exists.
// But first set the function pointer to NULL so that the fall-through below actually works.
*function = nullptr;
// NOTE: ActiveLoaderInstance cannot be used in this function because it is called before an instance is made active.
if (0 == strcmp(name, "xrGetInstanceProcAddr")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermGetInstanceProcAddr);
} else if (0 == strcmp(name, "xrCreateInstance")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateInstance);
} else if (0 == strcmp(name, "xrDestroyInstance")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyInstance);
} else if (0 == strcmp(name, "xrSetDebugUtilsObjectNameEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSetDebugUtilsObjectNameEXT);
} else if (0 == strcmp(name, "xrCreateDebugUtilsMessengerEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateDebugUtilsMessengerEXT);
} else if (0 == strcmp(name, "xrDestroyDebugUtilsMessengerEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermDestroyDebugUtilsMessengerEXT);
} else if (0 == strcmp(name, "xrSubmitDebugUtilsMessageEXT")) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermSubmitDebugUtilsMessageEXT);
} else if (0 == strcmp(name, "xrCreateApiLayerInstance")) {
// Special layer version of xrCreateInstance terminator. If we get called this by a layer,
// we simply re-direct the information back into the standard xrCreateInstance terminator.
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrTermCreateApiLayerInstance);
}
if (nullptr != *function) {
return XR_SUCCESS;
}
return RuntimeInterface::GetInstanceProcAddr(instance, name, function);
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Extension manual loader trampoline functions
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineCreateDebugUtilsMessengerEXT(XrInstance instance, const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader trampoline");
if (instance == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrCreateDebugUtilsMessengerEXT", "Instance handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrCreateDebugUtilsMessengerEXT");
if (XR_FAILED(result)) {
return result;
}
result = loader_instance->DispatchTable()->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader trampoline");
return result;
}
XRLOADER_ABI_CATCH_BAD_ALLOC_OOM XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
// TODO: get instance from messenger in loader
// Also, is the loader really doing all this every call?
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader trampoline");
if (messenger == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrDestroyDebugUtilsMessengerEXT", "Messenger handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrDestroyDebugUtilsMessengerEXT");
if (XR_FAILED(result)) {
return result;
}
result = loader_instance->DispatchTable()->DestroyDebugUtilsMessengerEXT(messenger);
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader trampoline");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionBeginDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
if (nullptr == labelInfo) {
LoaderLogger::LogValidationErrorMessage("VUID-xrSessionBeginDebugUtilsLabelRegionEXT-labelInfo-parameter",
"xrSessionBeginDebugUtilsLabelRegionEXT", "labelInfo must be non-NULL",
{XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionBeginDebugUtilsLabelRegionEXT");
if (XR_FAILED(result)) {
return result;
}
LoaderLogger::GetInstance().BeginLabelRegion(session, labelInfo);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionBeginDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionBeginDebugUtilsLabelRegionEXT(session, labelInfo);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT(XrSession session) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionEndDebugUtilsLabelRegionEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionEndDebugUtilsLabelRegionEXT");
if (XR_FAILED(result)) {
return result;
}
LoaderLogger::GetInstance().EndLabelRegion(session);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionEndDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionEndDebugUtilsLabelRegionEXT(session);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSessionInsertDebugUtilsLabelEXT(XrSession session, const XrDebugUtilsLabelEXT *labelInfo) XRLOADER_ABI_TRY {
if (session == XR_NULL_HANDLE) {
LoaderLogger::LogErrorMessage("xrSessionInsertDebugUtilsLabelEXT", "Session handle is XR_NULL_HANDLE.");
return XR_ERROR_HANDLE_INVALID;
}
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSessionInsertDebugUtilsLabelEXT");
if (XR_FAILED(result)) {
return result;
}
if (nullptr == labelInfo) {
LoaderLogger::LogValidationErrorMessage("VUID-xrSessionInsertDebugUtilsLabelEXT-labelInfo-parameter",
"xrSessionInsertDebugUtilsLabelEXT", "labelInfo must be non-NULL",
{XrSdkLogObjectInfo{session, XR_OBJECT_TYPE_SESSION}});
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderLogger::GetInstance().InsertLabel(session, labelInfo);
const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionInsertDebugUtilsLabelEXT) {
return dispatch_table->SessionInsertDebugUtilsLabelEXT(session, labelInfo);
}
return XR_SUCCESS;
}
XRLOADER_ABI_CATCH_FALLBACK
// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
static XRAPI_ATTR XrResult XRAPI_CALL
LoaderTrampolineSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSetDebugUtilsObjectNameEXT");
if (XR_SUCCEEDED(result)) {
result = loader_instance->DispatchTable()->SetDebugUtilsObjectNameEXT(instance, nameInfo);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
// No-op trampoline needed for xrGetInstanceProcAddr. Work done in terminator.
static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
LoaderInstance *loader_instance;
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrSubmitDebugUtilsMessageEXT");
if (XR_SUCCEEDED(result)) {
result =
loader_instance->DispatchTable()->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
}
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
// ---- Extension manual loader terminator functions
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstance instance,
const XrDebugUtilsMessengerCreateInfoEXT *createInfo,
XrDebugUtilsMessengerEXT *messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Entering loader terminator");
if (nullptr == messenger) {
LoaderLogger::LogValidationErrorMessage("VUID-xrCreateDebugUtilsMessengerEXT-messenger-parameter",
"xrCreateDebugUtilsMessengerEXT", "invalid messenger pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
// This extension is supported entirely by the loader which means the runtime may or may not support it.
if (nullptr != dispatch_table->CreateDebugUtilsMessengerEXT) {
result = dispatch_table->CreateDebugUtilsMessengerEXT(instance, createInfo, messenger);
} else {
// Just allocate a character so we have a unique value
char *temp_mess_ptr = new char;
*messenger = reinterpret_cast<XrDebugUtilsMessengerEXT>(temp_mess_ptr);
}
if (XR_SUCCEEDED(result)) {
LoaderLogger::GetInstance().AddLogRecorderForXrInstance(instance, MakeDebugUtilsLoaderLogRecorder(createInfo, *messenger));
RuntimeInterface::GetRuntime().TrackDebugMessenger(instance, *messenger);
}
LoaderLogger::LogVerboseMessage("xrCreateDebugUtilsMessengerEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDebugUtilsMessengerDispatchTable(messenger);
XrResult result = XR_SUCCESS;
LoaderLogger::GetInstance().RemoveLogRecorder(MakeHandleGeneric(messenger));
RuntimeInterface::GetRuntime().ForgetDebugMessenger(messenger);
// This extension is supported entirely by the loader which means the runtime may or may not support it.
if (nullptr != dispatch_table->DestroyDebugUtilsMessengerEXT) {
result = dispatch_table->DestroyDebugUtilsMessengerEXT(messenger);
} else {
// Delete the character we would've created
delete (reinterpret_cast<char *>(MakeHandleGeneric(messenger)));
}
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SubmitDebugUtilsMessageEXT) {
result = dispatch_table->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
} else {
// Only log the message from the loader if the runtime doesn't support this extension. If we did,
// then the user would receive multiple instances of the same message.
LoaderLogger::GetInstance().LogDebugUtilsMessage(messageSeverity, messageTypes, callbackData);
}
LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Entering loader terminator");
const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SetDebugUtilsObjectNameEXT) {
result = dispatch_table->SetDebugUtilsObjectNameEXT(instance, nameInfo);
}
LoaderLogger::GetInstance().AddObjectName(nameInfo->objectHandle, nameInfo->objectType, nameInfo->objectName);
LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Completed loader terminator");
return result;
}
XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) XRLOADER_ABI_TRY {
// Initialize the function to nullptr in case it does not get caught in a known case
*function = nullptr;
if (nullptr == function) {
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
"Invalid Function pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
if (nullptr == name) {
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-function-parameter", "xrGetInstanceProcAddr",
"Invalid Name pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
LoaderInstance *loader_instance = nullptr;
if (instance == XR_NULL_HANDLE) {
// Null instance is allowed for a few specific API entry points, otherwise return error
if (strcmp(name, "xrCreateInstance") != 0 && strcmp(name, "xrEnumerateApiLayerProperties") != 0 &&
strcmp(name, "xrEnumerateInstanceExtensionProperties") != 0 && strcmp(name, "xrInitializeLoaderKHR") != 0) {
// TODO why is xrGetInstanceProcAddr not listed in here?
std::string error_str = "XR_NULL_HANDLE for instance but query for ";
error_str += name;
error_str += " requires a valid instance";
LoaderLogger::LogValidationErrorMessage("VUID-xrGetInstanceProcAddr-instance-parameter", "xrGetInstanceProcAddr",
error_str);
return XR_ERROR_HANDLE_INVALID;
}
} else {
// non null instance passed in, it should be our current instance
XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrGetInstanceProcAddr");
if (XR_FAILED(result)) {
return result;
}
if (loader_instance->GetInstanceHandle() != instance) {
return XR_ERROR_HANDLE_INVALID;
}
}
// These functions must always go through the loader's implementation (trampoline).
if (strcmp(name, "xrGetInstanceProcAddr") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrGetInstanceProcAddr);
return XR_SUCCESS;
} else if (strcmp(name, "xrInitializeLoaderKHR") == 0) {
#ifdef XR_KHR_LOADER_INIT_SUPPORT
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrInitializeLoaderKHR);
return XR_SUCCESS;
#else
return XR_ERROR_FUNCTION_UNSUPPORTED;
#endif
} else if (strcmp(name, "xrEnumerateApiLayerProperties") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateApiLayerProperties);
return XR_SUCCESS;
} else if (strcmp(name, "xrEnumerateInstanceExtensionProperties") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrEnumerateInstanceExtensionProperties);
return XR_SUCCESS;
} else if (strcmp(name, "xrCreateInstance") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrCreateInstance);
return XR_SUCCESS;
} else if (strcmp(name, "xrDestroyInstance") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderXrDestroyInstance);
return XR_SUCCESS;
}
// XR_EXT_debug_utils is built into the loader and handled partly through the xrGetInstanceProcAddress terminator,
// but the check to see if the extension is enabled must be done here where ActiveLoaderInstance is safe to use.
if (*function == nullptr) {
if (strcmp(name, "xrCreateDebugUtilsMessengerEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineCreateDebugUtilsMessengerEXT);
} else if (strcmp(name, "xrDestroyDebugUtilsMessengerEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineDestroyDebugUtilsMessengerEXT);
} else if (strcmp(name, "xrSessionBeginDebugUtilsLabelRegionEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT);
} else if (strcmp(name, "xrSessionEndDebugUtilsLabelRegionEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionEndDebugUtilsLabelRegionEXT);
} else if (strcmp(name, "xrSessionInsertDebugUtilsLabelEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSessionInsertDebugUtilsLabelEXT);
} else if (strcmp(name, "xrSetDebugUtilsObjectNameEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSetDebugUtilsObjectNameEXT);
} else if (strcmp(name, "xrSubmitDebugUtilsMessageEXT") == 0) {
*function = reinterpret_cast<PFN_xrVoidFunction>(LoaderTrampolineSubmitDebugUtilsMessageEXT);
}
if (*function != nullptr && !loader_instance->ExtensionIsEnabled("XR_EXT_debug_utils")) {
// The function matches one of the XR_EXT_debug_utils functions but the extension is not enabled.
*function = nullptr;
return XR_ERROR_FUNCTION_UNSUPPORTED;
}
}
if (*function != nullptr) {
// The loader has a trampoline or implementation of this function.
return XR_SUCCESS;
}
// If the function is not supported by the loader, call down to the next layer.
return loader_instance->GetInstanceProcAddr(name, function);
}
XRLOADER_ABI_CATCH_FALLBACK
// Exported loader functions
//
// The application might override these by exporting the same symbols and so we can't use these
// symbols anywhere in the loader code, and instead the internal non exported functions that these
// stubs call should be used internally.
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateApiLayerProperties(uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrApiLayerProperties *properties) {
return LoaderXrEnumerateApiLayerProperties(propertyCapacityInput, propertyCountOutput, properties);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateInstanceExtensionProperties(const char *layerName,
uint32_t propertyCapacityInput,
uint32_t *propertyCountOutput,
XrExtensionProperties *properties) {
return LoaderXrEnumerateInstanceExtensionProperties(layerName, propertyCapacityInput, propertyCountOutput, properties);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrCreateInstance(const XrInstanceCreateInfo *info, XrInstance *instance) {
return LoaderXrCreateInstance(info, instance);
}
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrDestroyInstance(XrInstance instance) { return LoaderXrDestroyInstance(instance); }
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrGetInstanceProcAddr(XrInstance instance, const char *name,
PFN_xrVoidFunction *function) {
return LoaderXrGetInstanceProcAddr(instance, name, function);
}
#ifdef XR_KHR_LOADER_INIT_SUPPORT
LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) {
return LoaderXrInitializeLoaderKHR(loaderInitInfo);
}
#endif

View File

@ -0,0 +1,303 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#include "loader_instance.hpp"
#include "api_layer_interface.hpp"
#include "hex_and_handles.h"
#include "loader_interfaces.h"
#include "loader_logger.hpp"
#include "runtime_interface.hpp"
#include "xr_generated_dispatch_table.h"
#include "xr_generated_loader.hpp"
#include <openxr/openxr.h>
#include <cstring>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace {
std::unique_ptr<LoaderInstance>& GetSetCurrentLoaderInstance() {
static std::unique_ptr<LoaderInstance> current_loader_instance;
return current_loader_instance;
}
} // namespace
namespace ActiveLoaderInstance {
XrResult Set(std::unique_ptr<LoaderInstance> loader_instance, const char* log_function_name) {
if (GetSetCurrentLoaderInstance() != nullptr) {
LoaderLogger::LogErrorMessage(log_function_name, "Active XrInstance handle already exists");
return XR_ERROR_LIMIT_REACHED;
}
GetSetCurrentLoaderInstance() = std::move(loader_instance);
return XR_SUCCESS;
}
XrResult Get(LoaderInstance** loader_instance, const char* log_function_name) {
*loader_instance = GetSetCurrentLoaderInstance().get();
if (*loader_instance == nullptr) {
LoaderLogger::LogErrorMessage(log_function_name, "No active XrInstance handle.");
return XR_ERROR_HANDLE_INVALID;
}
return XR_SUCCESS;
}
bool IsAvailable() { return GetSetCurrentLoaderInstance() != nullptr; }
void Remove() { GetSetCurrentLoaderInstance().release(); }
} // namespace ActiveLoaderInstance
// Extensions that are supported by the loader, but may not be supported
// the the runtime.
const std::array<XrExtensionProperties, 1>& LoaderInstance::LoaderSpecificExtensions() {
static const std::array<XrExtensionProperties, 1> extensions = {XrExtensionProperties{
XR_TYPE_EXTENSION_PROPERTIES, nullptr, XR_EXT_DEBUG_UTILS_EXTENSION_NAME, XR_EXT_debug_utils_SPEC_VERSION}};
return extensions;
}
namespace {
class InstanceCreateInfoManager {
public:
explicit InstanceCreateInfoManager(const XrInstanceCreateInfo* info) : original_create_info(info), modified_create_info(*info) {
Reset();
}
// Reset the "modified" state to match the original state.
void Reset() {
enabled_extensions_cstr.clear();
enabled_extensions_cstr.reserve(original_create_info->enabledExtensionCount);
for (uint32_t i = 0; i < original_create_info->enabledExtensionCount; ++i) {
enabled_extensions_cstr.push_back(original_create_info->enabledExtensionNames[i]);
}
Update();
}
// Remove extensions named in the parameter and return a pointer to the current state.
const XrInstanceCreateInfo* FilterOutExtensions(const std::vector<const char*>& extensions_to_skip) {
if (enabled_extensions_cstr.empty()) {
return Get();
}
if (extensions_to_skip.empty()) {
return Get();
}
for (auto& ext : extensions_to_skip) {
FilterOutExtension(ext);
}
return Update();
}
// Remove the extension named in the parameter and return a pointer to the current state.
const XrInstanceCreateInfo* FilterOutExtension(const char* extension_to_skip) {
if (enabled_extensions_cstr.empty()) {
return &modified_create_info;
}
auto b = enabled_extensions_cstr.begin();
auto e = enabled_extensions_cstr.end();
auto it = std::find_if(b, e, [&](const char* extension) { return strcmp(extension_to_skip, extension) == 0; });
if (it != e) {
// Just that one element goes away
enabled_extensions_cstr.erase(it);
}
return Update();
}
// Get the current modified XrInstanceCreateInfo
const XrInstanceCreateInfo* Get() const { return &modified_create_info; }
private:
const XrInstanceCreateInfo* Update() {
modified_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size());
modified_create_info.enabledExtensionNames = enabled_extensions_cstr.empty() ? nullptr : enabled_extensions_cstr.data();
return &modified_create_info;
}
const XrInstanceCreateInfo* original_create_info;
XrInstanceCreateInfo modified_create_info;
std::vector<const char*> enabled_extensions_cstr;
};
} // namespace
// Factory method
XrResult LoaderInstance::CreateInstance(PFN_xrGetInstanceProcAddr get_instance_proc_addr_term,
PFN_xrCreateInstance create_instance_term,
PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces,
const XrInstanceCreateInfo* info, std::unique_ptr<LoaderInstance>* loader_instance) {
LoaderLogger::LogVerboseMessage("xrCreateInstance", "Entering LoaderInstance::CreateInstance");
// Check the list of enabled extensions to make sure something supports them, and, if we do,
// add it to the list of enabled extensions
XrResult last_error = XR_SUCCESS;
for (uint32_t ext = 0; ext < info->enabledExtensionCount; ++ext) {
bool found = false;
// First check the runtime
if (RuntimeInterface::GetRuntime().SupportsExtension(info->enabledExtensionNames[ext])) {
found = true;
}
// Next check the loader
if (!found) {
for (auto& loader_extension : LoaderInstance::LoaderSpecificExtensions()) {
if (strcmp(loader_extension.extensionName, info->enabledExtensionNames[ext]) == 0) {
found = true;
break;
}
}
}
// Finally, check the enabled layers
if (!found) {
for (auto& layer_interface : api_layer_interfaces) {
if (layer_interface->SupportsExtension(info->enabledExtensionNames[ext])) {
found = true;
break;
}
}
}
if (!found) {
std::string msg = "LoaderInstance::CreateInstance, no support found for requested extension: ";
msg += info->enabledExtensionNames[ext];
LoaderLogger::LogErrorMessage("xrCreateInstance", msg);
last_error = XR_ERROR_EXTENSION_NOT_PRESENT;
break;
}
}
// Topmost means "closest to the application"
PFN_xrGetInstanceProcAddr topmost_gipa = get_instance_proc_addr_term;
XrInstance instance{XR_NULL_HANDLE};
if (XR_SUCCEEDED(last_error)) {
// Remove the loader-supported-extensions (debug utils), if it's in the list of enabled extensions but not supported by
// the runtime.
InstanceCreateInfoManager create_info_manager{info};
const XrInstanceCreateInfo* modified_create_info = info;
if (info->enabledExtensionCount > 0) {
std::vector<const char*> extensions_to_skip;
for (const auto& ext : LoaderInstance::LoaderSpecificExtensions()) {
if (!RuntimeInterface::GetRuntime().SupportsExtension(ext.extensionName)) {
extensions_to_skip.emplace_back(ext.extensionName);
}
}
modified_create_info = create_info_manager.FilterOutExtensions(extensions_to_skip);
}
// Only start the xrCreateApiLayerInstance stack if we have layers.
if (!api_layer_interfaces.empty()) {
// Initialize an array of ApiLayerNextInfo structs
std::unique_ptr<XrApiLayerNextInfo[]> next_info_list(new XrApiLayerNextInfo[api_layer_interfaces.size()]);
auto ni_index = static_cast<uint32_t>(api_layer_interfaces.size() - 1);
for (uint32_t i = 0; i <= ni_index; i++) {
next_info_list[i].structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO;
next_info_list[i].structVersion = XR_API_LAYER_NEXT_INFO_STRUCT_VERSION;
next_info_list[i].structSize = sizeof(XrApiLayerNextInfo);
}
// Go through all layers, and override the instance pointers with the layer version. However,
// go backwards through the layer list so we replace in reverse order so the layers can call their next function
// appropriately.
PFN_xrCreateApiLayerInstance topmost_cali_fp = create_api_layer_instance_term;
XrApiLayerNextInfo* topmost_nextinfo = nullptr;
for (auto layer_interface = api_layer_interfaces.rbegin(); layer_interface != api_layer_interfaces.rend();
++layer_interface) {
// Collect current layer's function pointers
PFN_xrGetInstanceProcAddr cur_gipa_fp = (*layer_interface)->GetInstanceProcAddrFuncPointer();
PFN_xrCreateApiLayerInstance cur_cali_fp = (*layer_interface)->GetCreateApiLayerInstanceFuncPointer();
// Fill in layer info and link previous (lower) layer fxn pointers
strncpy(next_info_list[ni_index].layerName, (*layer_interface)->LayerName().c_str(),
XR_MAX_API_LAYER_NAME_SIZE - 1);
next_info_list[ni_index].layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
next_info_list[ni_index].next = topmost_nextinfo;
next_info_list[ni_index].nextGetInstanceProcAddr = topmost_gipa;
next_info_list[ni_index].nextCreateApiLayerInstance = topmost_cali_fp;
// Update saved pointers for next iteration
topmost_nextinfo = &next_info_list[ni_index];
topmost_gipa = cur_gipa_fp;
topmost_cali_fp = cur_cali_fp;
ni_index--;
}
// Populate the ApiLayerCreateInfo struct and pass to topmost CreateApiLayerInstance()
XrApiLayerCreateInfo api_layer_ci = {};
api_layer_ci.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO;
api_layer_ci.structVersion = XR_API_LAYER_CREATE_INFO_STRUCT_VERSION;
api_layer_ci.structSize = sizeof(XrApiLayerCreateInfo);
api_layer_ci.loaderInstance = nullptr; // Not used.
api_layer_ci.settings_file_location[0] = '\0';
api_layer_ci.nextInfo = next_info_list.get();
//! @todo do we filter our create info extension list here?
//! Think that actually each layer might need to filter...
last_error = topmost_cali_fp(modified_create_info, &api_layer_ci, &instance);
} else {
// The loader's terminator is the topmost CreateInstance if there are no layers.
last_error = create_instance_term(modified_create_info, &instance);
}
if (XR_FAILED(last_error)) {
LoaderLogger::LogErrorMessage("xrCreateInstance", "LoaderInstance::CreateInstance chained CreateInstance call failed");
}
}
if (XR_SUCCEEDED(last_error)) {
loader_instance->reset(new LoaderInstance(instance, info, topmost_gipa, std::move(api_layer_interfaces)));
std::ostringstream oss;
oss << "LoaderInstance::CreateInstance succeeded with ";
oss << (*loader_instance)->LayerInterfaces().size();
oss << " layers enabled and runtime interface - created instance = ";
oss << HandleToHexString((*loader_instance)->GetInstanceHandle());
LoaderLogger::LogInfoMessage("xrCreateInstance", oss.str());
}
return last_error;
}
XrResult LoaderInstance::GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function) {
return _topmost_gipa(_runtime_instance, name, function);
}
LoaderInstance::LoaderInstance(XrInstance instance, const XrInstanceCreateInfo* create_info, PFN_xrGetInstanceProcAddr topmost_gipa,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces)
: _runtime_instance(instance),
_topmost_gipa(topmost_gipa),
_api_layer_interfaces(std::move(api_layer_interfaces)),
_dispatch_table(new XrGeneratedDispatchTable{}) {
for (uint32_t ext = 0; ext < create_info->enabledExtensionCount; ++ext) {
_enabled_extensions.push_back(create_info->enabledExtensionNames[ext]);
}
GeneratedXrPopulateDispatchTable(_dispatch_table.get(), instance, topmost_gipa);
}
LoaderInstance::~LoaderInstance() {
std::ostringstream oss;
oss << "Destroying LoaderInstance = ";
oss << PointerToHexString(this);
LoaderLogger::LogInfoMessage("xrDestroyInstance", oss.str());
}
bool LoaderInstance::ExtensionIsEnabled(const std::string& extension) {
for (std::string& cur_enabled : _enabled_extensions) {
if (cur_enabled == extension) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,77 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#pragma once
#include "extra_algorithms.h"
#include "loader_interfaces.h"
#include <openxr/openxr.h>
#include <array>
#include <cmath>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
class ApiLayerInterface;
struct XrGeneratedDispatchTable;
class LoaderInstance;
// Manage the single loader instance that is available.
namespace ActiveLoaderInstance {
// Set the active loader instance. This will fail if there is already an active loader instance.
XrResult Set(std::unique_ptr<LoaderInstance> loader_instance, const char* log_function_name);
// Returns true if there is an active loader instance.
bool IsAvailable();
// Get the active LoaderInstance.
XrResult Get(LoaderInstance** loader_instance, const char* log_function_name);
// Destroy the currently active LoaderInstance if there is one. This will make the loader able to create a new XrInstance if needed.
void Remove();
}; // namespace ActiveLoaderInstance
// Manages information needed by the loader for an XrInstance, such as what extensions are available and the dispatch table.
class LoaderInstance {
public:
// Factory method
static XrResult CreateInstance(PFN_xrGetInstanceProcAddr get_instance_proc_addr_term, PFN_xrCreateInstance create_instance_term,
PFN_xrCreateApiLayerInstance create_api_layer_instance_term,
std::vector<std::unique_ptr<ApiLayerInterface>> layer_interfaces,
const XrInstanceCreateInfo* createInfo, std::unique_ptr<LoaderInstance>* loader_instance);
static const std::array<XrExtensionProperties, 1>& LoaderSpecificExtensions();
virtual ~LoaderInstance();
XrInstance GetInstanceHandle() { return _runtime_instance; }
const std::unique_ptr<XrGeneratedDispatchTable>& DispatchTable() { return _dispatch_table; }
std::vector<std::unique_ptr<ApiLayerInterface>>& LayerInterfaces() { return _api_layer_interfaces; }
bool ExtensionIsEnabled(const std::string& extension);
XrDebugUtilsMessengerEXT DefaultDebugUtilsMessenger() { return _messenger; }
void SetDefaultDebugUtilsMessenger(XrDebugUtilsMessengerEXT messenger) { _messenger = messenger; }
XrResult GetInstanceProcAddr(const char* name, PFN_xrVoidFunction* function);
private:
LoaderInstance(XrInstance instance, const XrInstanceCreateInfo* createInfo, PFN_xrGetInstanceProcAddr topmost_gipa,
std::vector<std::unique_ptr<ApiLayerInterface>> api_layer_interfaces);
private:
XrInstance _runtime_instance{XR_NULL_HANDLE};
PFN_xrGetInstanceProcAddr _topmost_gipa{nullptr};
std::vector<std::string> _enabled_extensions;
std::vector<std::unique_ptr<ApiLayerInterface>> _api_layer_interfaces;
std::unique_ptr<XrGeneratedDispatchTable> _dispatch_table;
// Internal debug messenger created during xrCreateInstance
XrDebugUtilsMessengerEXT _messenger{XR_NULL_HANDLE};
};

View File

@ -0,0 +1,239 @@
// Copyright (c) 2017-2022, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// Initial Author: Mark Young <marky@lunarg.com>
//
#include "loader_logger.hpp"
#include "extra_algorithms.h"
#include "hex_and_handles.h"
#include "loader_logger_recorders.hpp"
#include "platform_utils.hpp"
#include <openxr/openxr.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
bool LoaderLogRecorder::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT /*message_severity*/,
XrDebugUtilsMessageTypeFlagsEXT /*message_type*/,
const XrDebugUtilsMessengerCallbackDataEXT* /*callback_data*/) {
return false;
}
// Utility functions for converting to/from XR_EXT_debug_utils values
XrLoaderLogMessageSeverityFlags DebugUtilsSeveritiesToLoaderLogMessageSeverities(
XrDebugUtilsMessageSeverityFlagsEXT utils_severities) {
XrLoaderLogMessageSeverityFlags log_severities = 0UL;
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT;
}
if ((utils_severities & XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0u) {
log_severities |= XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT;
}
return log_severities;
}
XrDebugUtilsMessageSeverityFlagsEXT LoaderLogMessageSeveritiesToDebugUtilsMessageSeverities(
XrLoaderLogMessageSeverityFlags log_severities) {
XrDebugUtilsMessageSeverityFlagsEXT utils_severities = 0UL;
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
}
if ((log_severities & XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT) != 0u) {
utils_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
}
return utils_severities;
}
XrLoaderLogMessageTypeFlagBits DebugUtilsMessageTypesToLoaderLogMessageTypes(XrDebugUtilsMessageTypeFlagsEXT utils_types) {
XrLoaderLogMessageTypeFlagBits log_types = 0UL;
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT;
}
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT;
}
if ((utils_types & XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) != 0u) {
log_types |= XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT;
}
return log_types;
}
XrDebugUtilsMessageTypeFlagsEXT LoaderLogMessageTypesToDebugUtilsMessageTypes(XrLoaderLogMessageTypeFlagBits log_types) {
XrDebugUtilsMessageTypeFlagsEXT utils_types = 0UL;
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
}
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
}
if ((log_types & XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT) != 0u) {
utils_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
}
return utils_types;
}
LoaderLogger::LoaderLogger() {
std::string debug_string = PlatformUtilsGetEnv("XR_LOADER_DEBUG");
// Add an error logger by default so that we at least get errors out to std::cerr.
// Normally we enable stderr output. But if the XR_LOADER_DEBUG environment variable is
// present as "none" then we don't.
if (debug_string != "none") {
AddLogRecorder(MakeStdErrLoaderLogRecorder(nullptr));
#ifdef __ANDROID__
// Add a logcat logger by default.
AddLogRecorder(MakeLogcatLoaderLogRecorder());
#endif // __ANDROID__
}
#ifdef _WIN32
// Add an debugger logger by default so that we at least get errors out to the debugger.
AddLogRecorder(MakeDebuggerLoaderLogRecorder(nullptr));
#endif
// If the environment variable to enable loader debugging is set, then enable the
// appropriate logging out to std::cout.
if (!debug_string.empty()) {
XrLoaderLogMessageSeverityFlags debug_flags = {};
if (debug_string == "error") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT;
} else if (debug_string == "warn") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT;
} else if (debug_string == "info") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT |
XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT;
} else if (debug_string == "all" || debug_string == "verbose") {
debug_flags = XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT |
XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT | XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT;
}
AddLogRecorder(MakeStdOutLoaderLogRecorder(nullptr, debug_flags));
}
}
void LoaderLogger::AddLogRecorder(std::unique_ptr<LoaderLogRecorder>&& recorder) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
_recorders.push_back(std::move(recorder));
}
void LoaderLogger::AddLogRecorderForXrInstance(XrInstance instance, std::unique_ptr<LoaderLogRecorder>&& recorder) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
_recordersByInstance[instance].insert(recorder->UniqueId());
_recorders.emplace_back(std::move(recorder));
}
void LoaderLogger::RemoveLogRecorder(uint64_t unique_id) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
vector_remove_if_and_erase(
_recorders, [=](std::unique_ptr<LoaderLogRecorder> const& recorder) { return recorder->UniqueId() == unique_id; });
for (auto& recorders : _recordersByInstance) {
auto& messengersForInstance = recorders.second;
if (messengersForInstance.count(unique_id) > 0) {
messengersForInstance.erase(unique_id);
}
}
}
void LoaderLogger::RemoveLogRecordersForXrInstance(XrInstance instance) {
std::unique_lock<std::shared_timed_mutex> lock(_mutex);
if (_recordersByInstance.find(instance) != _recordersByInstance.end()) {
auto recorders = _recordersByInstance[instance];
vector_remove_if_and_erase(_recorders, [=](std::unique_ptr<LoaderLogRecorder> const& recorder) {
return recorders.find(recorder->UniqueId()) != recorders.end();
});
_recordersByInstance.erase(instance);
}
}
bool LoaderLogger::LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
const std::string& message_id, const std::string& command_name, const std::string& message,
const std::vector<XrSdkLogObjectInfo>& objects) {
XrLoaderLogMessengerCallbackData callback_data = {};
callback_data.message_id = message_id.c_str();
callback_data.command_name = command_name.c_str();
callback_data.message = message.c_str();
auto names_and_labels = data_.PopulateNamesAndLabels(objects);
callback_data.objects = names_and_labels.sdk_objects.empty() ? nullptr : names_and_labels.sdk_objects.data();
callback_data.object_count = static_cast<uint8_t>(names_and_labels.objects.size());
callback_data.session_labels = names_and_labels.labels.empty() ? nullptr : names_and_labels.labels.data();
callback_data.session_labels_count = static_cast<uint8_t>(names_and_labels.labels.size());
std::shared_lock<std::shared_timed_mutex> lock(_mutex);
bool exit_app = false;
for (std::unique_ptr<LoaderLogRecorder>& recorder : _recorders) {
if ((recorder->MessageSeverities() & message_severity) == message_severity &&
(recorder->MessageTypes() & message_type) == message_type) {
exit_app |= recorder->LogMessage(message_severity, message_type, &callback_data);
}
}
return exit_app;
}
// Extension-specific logging functions
bool LoaderLogger::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity,
XrDebugUtilsMessageTypeFlagsEXT message_type,
const XrDebugUtilsMessengerCallbackDataEXT* callback_data) {
bool exit_app = false;
XrLoaderLogMessageSeverityFlags log_message_severity = DebugUtilsSeveritiesToLoaderLogMessageSeverities(message_severity);
XrLoaderLogMessageTypeFlags log_message_type = DebugUtilsMessageTypesToLoaderLogMessageTypes(message_type);
AugmentedCallbackData augmented_data;
data_.WrapCallbackData(&augmented_data, callback_data);
// Loop through the recorders
std::shared_lock<std::shared_timed_mutex> lock(_mutex);
for (std::unique_ptr<LoaderLogRecorder>& recorder : _recorders) {
// Only send the message if it's a debug utils recorder and of the type the recorder cares about.
if (recorder->Type() != XR_LOADER_LOG_DEBUG_UTILS ||
(recorder->MessageSeverities() & log_message_severity) != log_message_severity ||
(recorder->MessageTypes() & log_message_type) != log_message_type) {
continue;
}
exit_app |= recorder->LogDebugUtilsMessage(message_severity, message_type, augmented_data.exported_data);
}
return exit_app;
}
void LoaderLogger::AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name) {
data_.AddObjectName(object_handle, object_type, object_name);
}
void LoaderLogger::BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT* label_info) {
data_.BeginLabelRegion(session, *label_info);
}
void LoaderLogger::EndLabelRegion(XrSession session) { data_.EndLabelRegion(session); }
void LoaderLogger::InsertLabel(XrSession session, const XrDebugUtilsLabelEXT* label_info) {
data_.InsertLabel(session, *label_info);
}
void LoaderLogger::DeleteSessionLabels(XrSession session) { data_.DeleteSessionLabels(session); }

Some files were not shown because too many files have changed in this diff Show More