Przeglądaj źródła

Add support for Direct3D 12 OpenXR backend.

This change adds support for running XR projects built with the `d3d12`
rendering backend. The XR backend hooks into the setup for the D3D12
render context in order to use the desired device and command queue for
submission to OpenXR. The XR backend takes care of importing the D3D12
swapchain images into the render context.

As part of this process, three issues are addressed:
- Ensuring that resource state transitions are only done on textures
  that require them.
- Enabling view instancing in the PSOs for multiview render passes.
- Addressing a bug in the D3D12 runtime where PSO creation may fail
  when front face detection is used.

Please refer to #86283 for additional discussions on the implementation
details.
Matthieu Bucchianeri 4 miesięcy temu
rodzic
commit
e3c215fc13

+ 45 - 0
drivers/d3d12/d3d12_hooks.cpp

@@ -0,0 +1,45 @@
+/**************************************************************************/
+/*  d3d12_hooks.cpp                                                       */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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 "d3d12_hooks.h"
+
+D3D12Hooks *D3D12Hooks::singleton = nullptr;
+
+D3D12Hooks::D3D12Hooks() {
+	if (singleton == nullptr) {
+		singleton = this;
+	}
+}
+
+D3D12Hooks::~D3D12Hooks() {
+	if (singleton == this) {
+		singleton = nullptr;
+	}
+}

+ 48 - 0
drivers/d3d12/d3d12_hooks.h

@@ -0,0 +1,48 @@
+/**************************************************************************/
+/*  d3d12_hooks.h                                                         */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "rendering_device_driver_d3d12.h"
+
+class D3D12Hooks {
+private:
+	static D3D12Hooks *singleton;
+
+public:
+	D3D12Hooks();
+	virtual ~D3D12Hooks();
+	virtual D3D_FEATURE_LEVEL get_feature_level() const = 0;
+	virtual LUID get_adapter_luid() const = 0;
+	virtual void set_device(ID3D12Device *p_device) = 0;
+	virtual void set_command_queue(ID3D12CommandQueue *p_queue) = 0;
+	virtual void cleanup_device() = 0;
+	static D3D12Hooks *get_singleton() { return singleton; }
+};

+ 2 - 0
drivers/d3d12/rendering_context_driver_d3d12.cpp

@@ -30,6 +30,8 @@
 
 #include "rendering_context_driver_d3d12.h"
 
+#include "d3d12_hooks.h"
+
 #include "core/config/engine.h"
 #include "core/config/project_settings.h"
 #include "core/string/ustring.h"

+ 174 - 5
drivers/d3d12/rendering_device_driver_d3d12.cpp

@@ -30,6 +30,8 @@
 
 #include "rendering_device_driver_d3d12.h"
 
+#include "d3d12_hooks.h"
+
 #include "core/config/project_settings.h"
 #include "core/io/marshalls.h"
 #include "servers/rendering/rendering_device.h"
@@ -1356,7 +1358,105 @@ RDD::TextureID RenderingDeviceDriverD3D12::texture_create(const TextureFormat &p
 }
 
 RDD::TextureID RenderingDeviceDriverD3D12::texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) {
-	ERR_FAIL_V_MSG(TextureID(), "Unimplemented!");
+	ID3D12Resource *texture = (ID3D12Resource *)p_native_texture;
+
+#if defined(_MSC_VER) || !defined(_WIN32)
+	const D3D12_RESOURCE_DESC base_resource_desc = texture->GetDesc();
+#else
+	D3D12_RESOURCE_DESC base_resource_desc;
+	texture->GetDesc(&base_resource_desc);
+#endif
+	CD3DX12_RESOURCE_DESC resource_desc(base_resource_desc);
+	D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
+	{
+		srv_desc.Format = RD_TO_D3D12_FORMAT[p_format].general_format;
+		srv_desc.ViewDimension = resource_desc.SampleDesc.Count == 1 ? RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV[p_type] : RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_SRV_MS[p_type];
+		srv_desc.Shader4ComponentMapping = _compute_component_mapping(TextureView{ p_format });
+
+		switch (srv_desc.ViewDimension) {
+			case D3D12_SRV_DIMENSION_TEXTURE1D: {
+				srv_desc.Texture1D.MipLevels = resource_desc.MipLevels;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: {
+				srv_desc.Texture1DArray.MipLevels = resource_desc.MipLevels;
+				srv_desc.Texture1DArray.ArraySize = p_array_layers;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE2D: {
+				srv_desc.Texture2D.MipLevels = resource_desc.MipLevels;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE2DMS: {
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: {
+				srv_desc.Texture2DArray.MipLevels = resource_desc.MipLevels;
+				srv_desc.Texture2DArray.ArraySize = p_array_layers;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY: {
+				srv_desc.Texture2DMSArray.ArraySize = p_array_layers;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: {
+				srv_desc.TextureCubeArray.MipLevels = resource_desc.MipLevels;
+				srv_desc.TextureCubeArray.NumCubes = p_array_layers / 6;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURE3D: {
+				srv_desc.Texture3D.MipLevels = resource_desc.MipLevels;
+			} break;
+			case D3D12_SRV_DIMENSION_TEXTURECUBE: {
+				srv_desc.TextureCube.MipLevels = resource_desc.MipLevels;
+			} break;
+			default: {
+			}
+		}
+	}
+
+	D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = {};
+	{
+		uav_desc.Format = RD_TO_D3D12_FORMAT[p_format].general_format;
+		uav_desc.ViewDimension = resource_desc.SampleDesc.Count == 1 ? RD_TEXTURE_TYPE_TO_D3D12_VIEW_DIMENSION_FOR_UAV[p_type] : D3D12_UAV_DIMENSION_UNKNOWN;
+
+		switch (uav_desc.ViewDimension) {
+			case D3D12_UAV_DIMENSION_TEXTURE1DARRAY: {
+				uav_desc.Texture1DArray.ArraySize = p_array_layers;
+			} break;
+			case D3D12_UAV_DIMENSION_TEXTURE2DARRAY: {
+				// Either for an actual 2D texture array, cubemap or cubemap array.
+				uav_desc.Texture2DArray.ArraySize = p_array_layers;
+			} break;
+			case D3D12_UAV_DIMENSION_TEXTURE3D: {
+				uav_desc.Texture3D.WSize = resource_desc.Depth();
+			} break;
+			default: {
+			}
+		}
+	}
+
+	TextureInfo *tex_info = VersatileResource::allocate<TextureInfo>(resources_allocator);
+	tex_info->resource = texture;
+	tex_info->owner_info.resource = nullptr; // Not allocated by us.
+	tex_info->owner_info.allocation = nullptr; // Not allocated by us.
+	tex_info->owner_info.states.subresource_states.resize(resource_desc.MipLevels * p_array_layers);
+	for (uint32_t i = 0; i < tex_info->owner_info.states.subresource_states.size(); i++) {
+		tex_info->owner_info.states.subresource_states[i] = !p_depth_stencil ? D3D12_RESOURCE_STATE_RENDER_TARGET : D3D12_RESOURCE_STATE_DEPTH_WRITE;
+	}
+	tex_info->states_ptr = &tex_info->owner_info.states;
+	tex_info->format = p_format;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+	tex_info->desc = *(CD3DX12_RESOURCE_DESC *)&resource_desc;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+	tex_info->base_layer = 0;
+	tex_info->layers = p_array_layers;
+	tex_info->base_mip = 0;
+	tex_info->mipmaps = resource_desc.MipLevels;
+	tex_info->view_descs.srv = srv_desc;
+	tex_info->view_descs.uav = uav_desc;
+#ifdef DEBUG_ENABLED
+	tex_info->created_from_extension = true;
+#endif
+	return TextureID(tex_info);
 }
 
 RDD::TextureID RenderingDeviceDriverD3D12::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) {
@@ -2077,6 +2177,10 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier(CommandBufferID p_cmd_
 		if (texture_info->main_texture) {
 			texture_info = texture_info->main_texture;
 		}
+		// Textures created for simultaneous access do not need explicit transitions.
+		if (texture_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) {
+			continue;
+		}
 		_rd_stages_and_access_to_d3d12(p_src_stages, texture_barrier_rd.prev_layout, texture_barrier_rd.src_access, texture_barrier_d3d12.SyncBefore, texture_barrier_d3d12.AccessBefore);
 		_rd_stages_and_access_to_d3d12(p_dst_stages, texture_barrier_rd.next_layout, texture_barrier_rd.dst_access, texture_barrier_d3d12.SyncAfter, texture_barrier_d3d12.AccessAfter);
 		texture_barrier_d3d12.LayoutBefore = _rd_texture_layout_to_d3d12_barrier_layout(texture_barrier_rd.prev_layout);
@@ -2126,7 +2230,9 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier(CommandBufferID p_cmd_
 		barrier_group.pTextureBarriers = texture_barriers.ptr();
 	}
 
-	cmd_list_7->Barrier(barrier_groups_count, barrier_groups);
+	if (barrier_groups_count) {
+		cmd_list_7->Barrier(barrier_groups_count, barrier_groups);
+	}
 }
 
 /****************/
@@ -2212,6 +2318,10 @@ RDD::CommandQueueID RenderingDeviceDriverD3D12::command_queue_create(CommandQueu
 	HRESULT res = device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(d3d_queue.GetAddressOf()));
 	ERR_FAIL_COND_V(!SUCCEEDED(res), CommandQueueID());
 
+	if (p_identify_as_main_queue && D3D12Hooks::get_singleton() != nullptr) {
+		D3D12Hooks::get_singleton()->set_command_queue(d3d_queue.Get());
+	}
+
 	CommandQueueInfo *command_queue = memnew(CommandQueueInfo);
 	command_queue->d3d_queue = d3d_queue;
 	return CommandQueueID(command_queue);
@@ -3141,6 +3251,7 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
 		}
 
 		// - Link NIR shaders.
+		bool can_use_multiview = D3D12Hooks::get_singleton() != nullptr;
 		for (int i = SHADER_STAGE_MAX - 1; i >= 0; i--) {
 			if (!stages_nir_shaders.has(i)) {
 				continue;
@@ -3153,6 +3264,26 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
 					break;
 				}
 			}
+			// There is a bug in the Direct3D runtime during creation of a PSO with view instancing. If a fragment
+			// shader uses front/back face detection (SV_IsFrontFace), its signature must include the pixel position
+			// builtin variable (SV_Position), otherwise an Internal Runtime error will occur.
+			if (i == SHADER_STAGE_FRAGMENT && can_use_multiview) {
+				const bool use_front_face =
+						nir_find_variable_with_location(shader, nir_var_shader_in, VARYING_SLOT_FACE) ||
+						(shader->info.inputs_read & VARYING_BIT_FACE) ||
+						nir_find_variable_with_location(shader, nir_var_system_value, SYSTEM_VALUE_FRONT_FACE) ||
+						BITSET_TEST(shader->info.system_values_read, SYSTEM_VALUE_FRONT_FACE);
+				const bool use_position =
+						nir_find_variable_with_location(shader, nir_var_shader_in, VARYING_SLOT_POS) ||
+						(shader->info.inputs_read & VARYING_BIT_POS) ||
+						nir_find_variable_with_location(shader, nir_var_system_value, SYSTEM_VALUE_FRAG_COORD) ||
+						BITSET_TEST(shader->info.system_values_read, SYSTEM_VALUE_FRAG_COORD);
+				if (use_front_face && !use_position) {
+					nir_variable *const pos = nir_variable_create(shader, nir_var_shader_in, glsl_vec4_type(), "gl_FragCoord");
+					pos->data.location = VARYING_SLOT_POS;
+					shader->info.inputs_read |= VARYING_BIT_POS;
+				}
+			}
 			if (prev_shader) {
 				bool requires_runtime_data = {};
 				dxil_spirv_nir_link(shader, prev_shader, &dxil_runtime_conf, &requires_runtime_data);
@@ -5615,7 +5746,7 @@ RDD::PipelineID RenderingDeviceDriverD3D12::render_pipeline_create(
 		VectorView<PipelineSpecializationConstant> p_specialization_constants) {
 	const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id;
 
-	CD3DX12_PIPELINE_STATE_STREAM pipeline_desc = {};
+	CD3DX12_PIPELINE_STATE_STREAM1 pipeline_desc = {};
 
 	const RenderPassInfo *pass_info = (const RenderPassInfo *)p_render_pass.id;
 	RenderPipelineInfo render_info;
@@ -5808,6 +5939,15 @@ RDD::PipelineID RenderingDeviceDriverD3D12::render_pipeline_create(
 
 	render_info.dyn_params.blend_constant = p_blend_state.blend_constant;
 
+	// Multiview
+	// We are using render target slices for each view.
+	const D3D12_VIEW_INSTANCE_LOCATION viewInstanceLocations[D3D12_MAX_VIEW_INSTANCE_COUNT] = { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 } };
+	if (pass_info->view_count > 1) {
+		(&pipeline_desc.ViewInstancingDesc)->ViewInstanceCount = pass_info->view_count;
+		(&pipeline_desc.ViewInstancingDesc)->Flags = D3D12_VIEW_INSTANCING_FLAG_NONE;
+		(&pipeline_desc.ViewInstancingDesc)->pViewInstanceLocations = viewInstanceLocations;
+	}
+
 	// Stages bytecodes + specialization constants.
 
 	pipeline_desc.pRootSignature = shader_info_in->root_signature.Get();
@@ -6308,6 +6448,9 @@ RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D
 }
 
 RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() {
+	if (D3D12Hooks::get_singleton() != nullptr) {
+		D3D12Hooks::get_singleton()->cleanup_device();
+	}
 	glsl_type_singleton_decref();
 }
 
@@ -6342,17 +6485,43 @@ Error RenderingDeviceDriverD3D12::_initialize_device() {
 		d3d_D3D12EnableExperimentalFeatures(1, experimental_features, nullptr, nullptr);
 	}
 
+	D3D_FEATURE_LEVEL requested_feature_level = D3D_FEATURE_LEVEL_11_0;
+	// Override the adapter and feature level if needed by the XR backend.
+	if (D3D12Hooks::get_singleton() != nullptr) {
+		const LUID adapter_luid = D3D12Hooks::get_singleton()->get_adapter_luid();
+		requested_feature_level = D3D12Hooks::get_singleton()->get_feature_level();
+		ComPtr<IDXGIAdapter1> desired_adapter;
+		for (UINT adapter_index = 0;; adapter_index++) {
+			// EnumAdapters1 will fail with DXGI_ERROR_NOT_FOUND when there are no more adapters to
+			// enumerate.
+			if (context_driver->dxgi_factory_get()->EnumAdapters1(adapter_index, desired_adapter.ReleaseAndGetAddressOf()) == DXGI_ERROR_NOT_FOUND) {
+				break;
+			}
+			DXGI_ADAPTER_DESC1 desc;
+			desired_adapter->GetDesc1(&desc);
+			if (!memcmp(&desc.AdapterLuid, &adapter_luid, sizeof(LUID))) {
+				break;
+			}
+		}
+		ERR_FAIL_NULL_V(desired_adapter, ERR_CANT_CREATE);
+		adapter = desired_adapter;
+	}
+
 	ID3D12DeviceFactory *device_factory = context_driver->device_factory_get();
 	if (device_factory != nullptr) {
-		res = device_factory->CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device.GetAddressOf()));
+		res = device_factory->CreateDevice(adapter.Get(), requested_feature_level, IID_PPV_ARGS(device.GetAddressOf()));
 	} else {
 		PFN_D3D12_CREATE_DEVICE d3d_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)(void *)GetProcAddress(context_driver->lib_d3d12, "D3D12CreateDevice");
 		ERR_FAIL_NULL_V(d3d_D3D12CreateDevice, ERR_CANT_CREATE);
 
-		res = d3d_D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device.GetAddressOf()));
+		res = d3d_D3D12CreateDevice(adapter.Get(), requested_feature_level, IID_PPV_ARGS(device.GetAddressOf()));
 	}
 	ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "D3D12CreateDevice failed with error " + vformat("0x%08ux", (uint64_t)res) + ".");
 
+	if (D3D12Hooks::get_singleton() != nullptr) {
+		D3D12Hooks::get_singleton()->set_device(device.Get());
+	}
+
 	if (context_driver->use_validation_layers()) {
 		ComPtr<ID3D12InfoQueue> info_queue;
 		res = device.As(&info_queue);

+ 3 - 0
drivers/d3d12/rendering_device_driver_d3d12.h

@@ -275,6 +275,9 @@ private:
 
 		UINT mapped_subresource = UINT_MAX;
 		SelfList<TextureInfo> pending_clear{ this };
+#ifdef DEBUG_ENABLED
+		bool created_from_extension = false;
+#endif
 	};
 	SelfList<TextureInfo>::List textures_pending_clear;
 

+ 2 - 0
modules/openxr/extensions/SCsub

@@ -17,5 +17,7 @@ if env["metal"]:
     env_openxr.add_source_files(module_obj, "platform/openxr_metal_extension.mm")
 if env["opengl3"] and env["platform"] != "macos":
     env_openxr.add_source_files(module_obj, "platform/openxr_opengl_extension.cpp")
+if env["d3d12"]:
+    env_openxr.add_source_files(module_obj, "platform/openxr_d3d12_extension.cpp")
 
 env.modules_sources += module_obj

+ 450 - 0
modules/openxr/extensions/platform/openxr_d3d12_extension.cpp

@@ -0,0 +1,450 @@
+/**************************************************************************/
+/*  openxr_d3d12_extension.cpp                                            */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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_d3d12_extension.h"
+
+#ifdef D3D12_ENABLED
+
+#include "../../openxr_util.h"
+
+#include "servers/rendering/rendering_server_globals.h"
+#include "servers/rendering_server.h"
+
+HashMap<String, bool *> OpenXRD3D12Extension::get_requested_extensions() {
+	HashMap<String, bool *> request_extensions;
+
+	request_extensions[XR_KHR_D3D12_ENABLE_EXTENSION_NAME] = nullptr;
+
+	return request_extensions;
+}
+
+void OpenXRD3D12Extension::on_instance_created(const XrInstance p_instance) {
+	// Obtain pointers to functions we're accessing here.
+	ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+	EXT_INIT_XR_FUNC(xrGetD3D12GraphicsRequirementsKHR);
+	EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
+}
+
+D3D_FEATURE_LEVEL OpenXRD3D12Extension::get_feature_level() const {
+	XrGraphicsRequirementsD3D12KHR d3d12_requirements = {
+		XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR, // type
+		nullptr, // next
+		{ 0, 0 }, // adapterLuid
+		(D3D_FEATURE_LEVEL)0 // minFeatureLevel
+	};
+
+	XrResult result = xrGetD3D12GraphicsRequirementsKHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &d3d12_requirements);
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to get Direct3D 12 graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return D3D_FEATURE_LEVEL_11_0; // Good default.
+	}
+
+	// #ifdef DEBUG
+	print_line("OpenXR: xrGetD3D12GraphicsRequirementsKHR:");
+	print_line(" - minFeatureLevel: ", (uint32_t)d3d12_requirements.minFeatureLevel);
+	// #endif
+
+	return d3d12_requirements.minFeatureLevel;
+}
+
+LUID OpenXRD3D12Extension::get_adapter_luid() const {
+	XrGraphicsRequirementsD3D12KHR d3d12_requirements = {
+		XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR, // type
+		nullptr, // next
+		{ 0, 0 }, // adapterLuid
+		(D3D_FEATURE_LEVEL)0 // minFeatureLevel
+	};
+
+	XrResult result = xrGetD3D12GraphicsRequirementsKHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &d3d12_requirements);
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to get Direct3D 12 graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return {};
+	}
+
+	return d3d12_requirements.adapterLuid;
+}
+
+void OpenXRD3D12Extension::set_device(ID3D12Device *p_device) {
+	graphics_device = p_device;
+}
+
+void OpenXRD3D12Extension::set_command_queue(ID3D12CommandQueue *p_queue) {
+	command_queue = p_queue;
+}
+
+void OpenXRD3D12Extension::cleanup_device() {
+	command_queue.Reset();
+	graphics_device.Reset();
+}
+
+XrGraphicsBindingD3D12KHR OpenXRD3D12Extension::graphics_binding_d3d12;
+
+void *OpenXRD3D12Extension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
+	DEV_ASSERT(graphics_device && "Graphics Device was not specified yet.");
+	DEV_ASSERT(command_queue && "Command queue was not specified yet.");
+
+	graphics_binding_d3d12.type = XR_TYPE_GRAPHICS_BINDING_D3D12_KHR,
+	graphics_binding_d3d12.next = p_next_pointer;
+	graphics_binding_d3d12.device = graphics_device.Get();
+	graphics_binding_d3d12.queue = command_queue.Get();
+
+	return &graphics_binding_d3d12;
+}
+
+void OpenXRD3D12Extension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
+	p_usable_swap_chains.push_back(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
+	p_usable_swap_chains.push_back(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
+	p_usable_swap_chains.push_back(DXGI_FORMAT_R8G8B8A8_UNORM);
+	p_usable_swap_chains.push_back(DXGI_FORMAT_B8G8R8A8_UNORM);
+}
+
+void OpenXRD3D12Extension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
+	p_usable_depth_formats.push_back(DXGI_FORMAT_D32_FLOAT);
+	p_usable_depth_formats.push_back(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
+	p_usable_depth_formats.push_back(DXGI_FORMAT_D24_UNORM_S8_UINT);
+	p_usable_depth_formats.push_back(DXGI_FORMAT_D16_UNORM);
+}
+
+bool OpenXRD3D12Extension::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) {
+	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 swapchain image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return false;
+	}
+
+	LocalVector<XrSwapchainImageD3D12KHR> images;
+	images.resize(swapchain_length);
+
+	for (XrSwapchainImageD3D12KHR &image : images) {
+		image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR;
+		image.next = nullptr;
+		image.texture = nullptr;
+	}
+
+	result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images.ptr());
+	if (XR_FAILED(result)) {
+		print_line("OpenXR: Failed to get swapchain images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+		return false;
+	}
+
+	SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
+	if (data == nullptr) {
+		print_line("OpenXR: Failed to allocate memory for swapchain data");
+		return false;
+	}
+	*r_swapchain_graphics_data = data;
+	data->is_multiview = (p_array_size > 1);
+
+	RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
+	RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
+	uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
+
+	switch (p_swapchain_format) {
+		case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+			// Even though this is an sRGB framebuffer format we're using UNORM here.
+			// The reason here is because Godot does a linear to sRGB conversion while
+			// with the sRGB format, this conversion would be doubled by the hardware.
+			// This also means we're reading the values as is for our preview on screen.
+			// The OpenXR runtime however is still treating this as an sRGB format and
+			// will thus do an sRGB -> Linear conversion as expected.
+			//format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
+			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+			//format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
+			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_R8G8B8A8_UNORM:
+			format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_B8G8R8A8_UNORM:
+			format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_D32_FLOAT:
+			format = RenderingDevice::DATA_FORMAT_D32_SFLOAT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
+			format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_D24_UNORM_S8_UINT:
+			format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		case DXGI_FORMAT_D16_UNORM:
+			format = RenderingDevice::DATA_FORMAT_D16_UNORM;
+			usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			break;
+		default:
+			// continue with our default value
+			print_line("OpenXR: Unsupported swapchain format", p_swapchain_format);
+			break;
+	}
+
+	switch (p_sample_count) {
+		case 1:
+			samples = RenderingDevice::TEXTURE_SAMPLES_1;
+			break;
+		case 2:
+			samples = RenderingDevice::TEXTURE_SAMPLES_2;
+			break;
+		case 4:
+			samples = RenderingDevice::TEXTURE_SAMPLES_4;
+			break;
+		case 8:
+			samples = RenderingDevice::TEXTURE_SAMPLES_8;
+			break;
+		case 16:
+			samples = RenderingDevice::TEXTURE_SAMPLES_16;
+			break;
+		case 32:
+			samples = RenderingDevice::TEXTURE_SAMPLES_32;
+			break;
+		case 64:
+			samples = RenderingDevice::TEXTURE_SAMPLES_64;
+			break;
+		default:
+			// continue with our default value
+			print_line("OpenXR: Unsupported sample count", p_sample_count);
+			break;
+	}
+
+	Vector<RID> texture_rids;
+
+	for (const XrSwapchainImageD3D12KHR &swapchain_image : images) {
+		RID texture_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)swapchain_image.texture,
+				p_width,
+				p_height,
+				1,
+				p_array_size);
+
+		texture_rids.push_back(texture_rid);
+	}
+
+	data->texture_rids = texture_rids;
+
+	return true;
+}
+
+bool OpenXRD3D12Extension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
+	OpenXRUtil::XrMatrix4x4f matrix;
+	OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_D3D, 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.columns[j][i] = matrix.m[j * 4 + i];
+		}
+	}
+
+	return true;
+}
+
+RID OpenXRD3D12Extension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
+	SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
+	ERR_FAIL_NULL_V(data, RID());
+
+	ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
+	return data->texture_rids[p_image_index];
+}
+
+void OpenXRD3D12Extension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
+	if (*p_swapchain_graphics_data == nullptr) {
+		return;
+	}
+
+	RenderingServer *rendering_server = RenderingServer::get_singleton();
+	ERR_FAIL_NULL(rendering_server);
+	RenderingDevice *rendering_device = rendering_server->get_rendering_device();
+	ERR_FAIL_NULL(rendering_device);
+
+	SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
+
+	for (const RID &texture_rid : data->texture_rids) {
+		// 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(texture_rid);
+	}
+	data->texture_rids.clear();
+
+	memdelete(data);
+	*p_swapchain_graphics_data = nullptr;
+}
+
+#define ENUM_TO_STRING_CASE(e) \
+	case e: {                  \
+		return String(#e);     \
+	} break;
+
+String OpenXRD3D12Extension::get_swapchain_format_name(int64_t p_swapchain_format) const {
+	// These are somewhat different per platform, will need to weed some stuff out...
+	switch (p_swapchain_format) {
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_UNKNOWN)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32A32_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32B32_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16B16A16_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G32_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32G8X24_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_D32_FLOAT_S8X24_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10A2_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R11G11B10_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8B8A8_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16G16_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_D32_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R32_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R24G8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_D24_UNORM_S8_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R24_UNORM_X8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_X24_TYPELESS_G8_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_FLOAT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_D16_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R16_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_UINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8_SINT)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_A8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R1_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R9G9B9E5_SHAREDEXP)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R8G8_B8G8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_G8R8_G8B8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC1_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC2_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC3_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC4_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC5_SNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B5G6R5_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B5G5R5A1_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_UF16)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC6H_SF16)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_TYPELESS)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_BC7_UNORM_SRGB)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_AYUV)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_Y410)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_Y416)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_NV12)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_P010)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_P016)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_420_OPAQUE)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_YUY2)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_Y210)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_Y216)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_NV11)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_AI44)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_IA44)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_P8)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_A8P8)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_B4G4R4A4_UNORM)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_P208)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_V208)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_V408)
+		ENUM_TO_STRING_CASE(DXGI_FORMAT_A4B4G4R4_UNORM)
+		default: {
+			return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
+		} break;
+	}
+}
+
+#endif // D3D12_ENABLED

+ 84 - 0
modules/openxr/extensions/platform/openxr_d3d12_extension.h

@@ -0,0 +1,84 @@
+/**************************************************************************/
+/*  openxr_d3d12_extension.h                                              */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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.                 */
+/**************************************************************************/
+
+#pragma once
+
+#ifdef D3D12_ENABLED
+
+#include "../../openxr_api.h"
+#include "../../util.h"
+#include "../openxr_extension_wrapper.h"
+
+#include "core/templates/vector.h"
+#include "drivers/d3d12/d3d12_hooks.h"
+
+// Always include this as late as possible.
+#include "../../openxr_platform_inc.h"
+
+class OpenXRD3D12Extension : public OpenXRGraphicsExtensionWrapper, D3D12Hooks {
+public:
+	virtual HashMap<String, bool *> get_requested_extensions() 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 D3D_FEATURE_LEVEL get_feature_level() const override final;
+	virtual LUID get_adapter_luid() const override final;
+	virtual void set_device(ID3D12Device *p_device) override final;
+	virtual void set_command_queue(ID3D12CommandQueue *p_queue) override final;
+	virtual void cleanup_device() override final;
+
+	virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
+	virtual void get_usable_depth_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, Projection &r_camera_matrix) override;
+	virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
+	virtual RID get_density_map(void *p_swapchain_graphics_data, int p_image_index) override { return RID(); }
+
+private:
+	static OpenXRD3D12Extension *singleton;
+
+	static XrGraphicsBindingD3D12KHR graphics_binding_d3d12;
+
+	struct SwapchainGraphicsData {
+		bool is_multiview;
+		Vector<RID> texture_rids;
+	};
+
+	EXT_PROTO_XRRESULT_FUNC3(xrGetD3D12GraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsD3D12KHR *), p_graphics_requirements)
+	EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
+
+	ComPtr<ID3D12Device> graphics_device;
+	ComPtr<ID3D12CommandQueue> command_queue;
+};
+
+#endif // D3D12_ENABLED

+ 12 - 0
modules/openxr/openxr_api.cpp

@@ -52,6 +52,10 @@
 #include "extensions/platform/openxr_opengl_extension.h"
 #endif
 
+#ifdef D3D12_ENABLED
+#include "extensions/platform/openxr_d3d12_extension.h"
+#endif
+
 #include "extensions/openxr_composition_layer_depth_extension.h"
 #include "extensions/openxr_debug_utils_extension.h"
 #include "extensions/openxr_eye_gaze_interaction.h"
@@ -1679,6 +1683,14 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
 #else
 		// shouldn't be possible...
 		ERR_FAIL_V(false);
+#endif
+	} else if (p_rendering_driver == "d3d12") {
+#ifdef D3D12_ENABLED
+		graphics_extension = memnew(OpenXRD3D12Extension);
+		register_extension_wrapper(graphics_extension);
+#else
+		// shouldn't be possible...
+		ERR_FAIL_V(false);
 #endif
 	} else {
 		ERR_FAIL_V_MSG(false, "OpenXR: Unsupported rendering device.");

+ 5 - 0
modules/openxr/openxr_platform_inc.h

@@ -68,6 +68,11 @@
 #endif // X11_ENABLED
 #endif // defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
 
+#ifdef D3D12_ENABLED
+#define XR_USE_GRAPHICS_API_D3D12
+#include "drivers/d3d12/rendering_context_driver_d3d12.h"
+#endif // D3D12_ENABLED
+
 #ifdef X11_ENABLED
 #include <X11/Xlib.h>
 #endif // X11_ENABLED

+ 1 - 1
servers/rendering/renderer_rd/shader_rd.cpp

@@ -435,7 +435,7 @@ String ShaderRD::_version_get_sha1(Version *p_version) const {
 }
 
 static const char *shader_file_header = "GDSC";
-static const uint32_t cache_file_version = 3;
+static const uint32_t cache_file_version = 4;
 
 String ShaderRD::_get_cache_file_path(Version *p_version, int p_group) {
 	const String &sha1 = _version_get_sha1(p_version);