Selaa lähdekoodia

Merge pull request #104207 from mbucchia/d3d12-xr

Add support for Direct3D 12 OpenXR backend
Thaddeus Crews 5 kuukautta sitten
vanhempi
commit
e5d1fdcfea

+ 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);