mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	
		
			
	
	
		
			1068 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1068 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | /**************************************************************************/ | ||
|  | /*  d3d12_context.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_context.h"
 | ||
|  | 
 | ||
|  | #include "core/config/engine.h"
 | ||
|  | #include "core/config/project_settings.h"
 | ||
|  | #include "core/string/ustring.h"
 | ||
|  | #include "core/templates/local_vector.h"
 | ||
|  | #include "core/version.h"
 | ||
|  | #include "servers/rendering/rendering_device.h"
 | ||
|  | 
 | ||
|  | #include "dxcapi.h"
 | ||
|  | 
 | ||
|  | extern "C" { | ||
|  | char godot_nir_arch_name[32]; | ||
|  | 
 | ||
|  | #ifdef AGILITY_SDK_ENABLED
 | ||
|  | __declspec(dllexport) extern const UINT D3D12SDKVersion = 610; | ||
|  | #ifdef AGILITY_SDK_MULTIARCH_ENABLED
 | ||
|  | #if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
 | ||
|  | __declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\arm64"; | ||
|  | #else
 | ||
|  | __declspec(dllexport) extern const char *D3D12SDKPath = "\\.\\x86_64"; | ||
|  | #endif
 | ||
|  | #else
 | ||
|  | __declspec(dllexport) extern const char *D3D12SDKPath = "\\."; | ||
|  | #endif // AGILITY_SDK_MULTIARCH
 | ||
|  | #endif // AGILITY_SDK_ENABLED
 | ||
|  | } | ||
|  | 
 | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | #define USE_PIX
 | ||
|  | #include "WinPixEventRuntime/pix3.h"
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 | ||
|  | 
 | ||
|  | void D3D12Context::_debug_message_func( | ||
|  | 		D3D12_MESSAGE_CATEGORY p_category, | ||
|  | 		D3D12_MESSAGE_SEVERITY p_severity, | ||
|  | 		D3D12_MESSAGE_ID p_id, | ||
|  | 		LPCSTR p_description, | ||
|  | 		void *p_context) { | ||
|  | 	String type_string; | ||
|  | 	switch (p_category) { | ||
|  | 		case D3D12_MESSAGE_CATEGORY_APPLICATION_DEFINED: | ||
|  | 			type_string = "APPLICATION_DEFINED"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_MISCELLANEOUS: | ||
|  | 			type_string = "MISCELLANEOUS"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_INITIALIZATION: | ||
|  | 			type_string = "INITIALIZATION"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_CLEANUP: | ||
|  | 			type_string = "CLEANUP"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_COMPILATION: | ||
|  | 			type_string = "COMPILATION"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_STATE_CREATION: | ||
|  | 			type_string = "STATE_CREATION"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_STATE_SETTING: | ||
|  | 			type_string = "STATE_SETTING"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_STATE_GETTING: | ||
|  | 			type_string = "STATE_GETTING"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: | ||
|  | 			type_string = "RESOURCE_MANIPULATION"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_EXECUTION: | ||
|  | 			type_string = "EXECUTION"; | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_CATEGORY_SHADER: | ||
|  | 			type_string = "SHADER"; | ||
|  | 			break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	String error_message(type_string + | ||
|  | 			" - Message Id Number: " + String::num_int64(p_id) + | ||
|  | 			"\n\t" + p_description); | ||
|  | 
 | ||
|  | 	// Convert D3D12 severity to our own log macros.
 | ||
|  | 	switch (p_severity) { | ||
|  | 		case D3D12_MESSAGE_SEVERITY_MESSAGE: | ||
|  | 			print_verbose(error_message); | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_SEVERITY_INFO: | ||
|  | 			print_line(error_message); | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_SEVERITY_WARNING: | ||
|  | 			WARN_PRINT(error_message); | ||
|  | 			break; | ||
|  | 		case D3D12_MESSAGE_SEVERITY_ERROR: | ||
|  | 		case D3D12_MESSAGE_SEVERITY_CORRUPTION: | ||
|  | 			ERR_PRINT(error_message); | ||
|  | 			CRASH_COND_MSG(Engine::get_singleton()->is_abort_on_gpu_errors_enabled(), | ||
|  | 					"Crashing, because abort on GPU errors is enabled."); | ||
|  | 			break; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | uint32_t D3D12Context::SubgroupCapabilities::supported_stages_flags_rd() const { | ||
|  | 	// If there's a way to check exactly which are supported, I have yet to find it.
 | ||
|  | 	return ( | ||
|  | 			RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT | | ||
|  | 			RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT); | ||
|  | } | ||
|  | 
 | ||
|  | uint32_t D3D12Context::SubgroupCapabilities::supported_operations_flags_rd() const { | ||
|  | 	if (!wave_ops_supported) { | ||
|  | 		return 0; | ||
|  | 	} else { | ||
|  | 		return ( | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT | | ||
|  | 				RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_check_capabilities() { | ||
|  | 	// Assume not supported until proven otherwise.
 | ||
|  | 	vrs_capabilities.draw_call_supported = false; | ||
|  | 	vrs_capabilities.primitive_supported = false; | ||
|  | 	vrs_capabilities.primitive_in_multiviewport = false; | ||
|  | 	vrs_capabilities.ss_image_supported = false; | ||
|  | 	vrs_capabilities.ss_image_tile_size = 1; | ||
|  | 	vrs_capabilities.additional_rates_supported = false; | ||
|  | 	multiview_capabilities.is_supported = false; | ||
|  | 	multiview_capabilities.geometry_shader_is_supported = false; | ||
|  | 	multiview_capabilities.tessellation_shader_is_supported = false; | ||
|  | 	multiview_capabilities.max_view_count = 0; | ||
|  | 	multiview_capabilities.max_instance_count = 0; | ||
|  | 	multiview_capabilities.is_supported = false; | ||
|  | 	subgroup_capabilities.size = 0; | ||
|  | 	subgroup_capabilities.wave_ops_supported = false; | ||
|  | 	shader_capabilities.shader_model = D3D_SHADER_MODEL_6_0; | ||
|  | 	shader_capabilities.native_16bit_ops = false; | ||
|  | 	storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = false; | ||
|  | 	format_capabilities.relaxed_casting_supported = false; | ||
|  | 
 | ||
|  | 	{ | ||
|  | 		D3D12_FEATURE_DATA_SHADER_MODEL shader_model = {}; | ||
|  | 		shader_model.HighestShaderModel = MIN(D3D_HIGHEST_SHADER_MODEL, D3D_SHADER_MODEL_6_6); | ||
|  | 		HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model)); | ||
|  | 		ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 		shader_capabilities.shader_model = shader_model.HighestShaderModel; | ||
|  | 	} | ||
|  | 	print_verbose("- Shader:"); | ||
|  | 	print_verbose("  model: " + itos(shader_capabilities.shader_model >> 4) + "." + itos(shader_capabilities.shader_model & 0xf)); | ||
|  | 
 | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; | ||
|  | 	HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)); | ||
|  | 	if (SUCCEEDED(res)) { | ||
|  | 		storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = options.TypedUAVLoadAdditionalFormats; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS1 options1 = {}; | ||
|  | 	res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1, &options1, sizeof(options1)); | ||
|  | 	if (SUCCEEDED(res)) { | ||
|  | 		subgroup_capabilities.size = options1.WaveLaneCountMin; | ||
|  | 		subgroup_capabilities.wave_ops_supported = options1.WaveOps; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {}; | ||
|  | 	res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)); | ||
|  | 	if (SUCCEEDED(res)) { | ||
|  | 		// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_view_instancing_tier
 | ||
|  | 		// https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html#sv_viewid
 | ||
|  | 		if (options3.ViewInstancingTier >= D3D12_VIEW_INSTANCING_TIER_1) { | ||
|  | 			multiview_capabilities.is_supported = true; | ||
|  | 			multiview_capabilities.geometry_shader_is_supported = options3.ViewInstancingTier >= D3D12_VIEW_INSTANCING_TIER_3; | ||
|  | 			multiview_capabilities.tessellation_shader_is_supported = options3.ViewInstancingTier >= D3D12_VIEW_INSTANCING_TIER_3; | ||
|  | 			multiview_capabilities.max_view_count = D3D12_MAX_VIEW_INSTANCE_COUNT; | ||
|  | 			multiview_capabilities.max_instance_count = UINT32_MAX; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = {}; | ||
|  | 	res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, sizeof(options6)); | ||
|  | 	if (SUCCEEDED(res)) { | ||
|  | 		if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_1) { | ||
|  | 			vrs_capabilities.draw_call_supported = true; | ||
|  | 			if (options6.VariableShadingRateTier >= D3D12_VARIABLE_SHADING_RATE_TIER_2) { | ||
|  | 				vrs_capabilities.primitive_supported = true; | ||
|  | 				vrs_capabilities.primitive_in_multiviewport = options6.PerPrimitiveShadingRateSupportedWithViewportIndexing; | ||
|  | 				vrs_capabilities.ss_image_supported = true; | ||
|  | 				vrs_capabilities.ss_image_tile_size = options6.ShadingRateImageTileSize; | ||
|  | 				vrs_capabilities.additional_rates_supported = options6.AdditionalShadingRatesSupported; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS12 options12 = {}; | ||
|  | 	res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &options12, sizeof(options12)); | ||
|  | 	if (SUCCEEDED(res)) { | ||
|  | 		format_capabilities.relaxed_casting_supported = options12.RelaxedFormatCastingSupported; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (vrs_capabilities.draw_call_supported || vrs_capabilities.primitive_supported || vrs_capabilities.ss_image_supported) { | ||
|  | 		print_verbose("- D3D12 Variable Rate Shading supported:"); | ||
|  | 		if (vrs_capabilities.draw_call_supported) { | ||
|  | 			print_verbose("  Draw call"); | ||
|  | 		} | ||
|  | 		if (vrs_capabilities.primitive_supported) { | ||
|  | 			print_verbose(String("  Per-primitive (multi-viewport: ") + (vrs_capabilities.primitive_in_multiviewport ? "yes" : "no") + ")"); | ||
|  | 		} | ||
|  | 		if (vrs_capabilities.ss_image_supported) { | ||
|  | 			print_verbose(String("  Screen-space image (tile size: ") + itos(vrs_capabilities.ss_image_tile_size) + ")"); | ||
|  | 		} | ||
|  | 		if (vrs_capabilities.additional_rates_supported) { | ||
|  | 			print_verbose(String("  Additional rates: ") + (vrs_capabilities.additional_rates_supported ? "yes" : "no")); | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		print_verbose("- D3D12 Variable Rate Shading not supported"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (multiview_capabilities.is_supported) { | ||
|  | 		print_verbose("- D3D12 multiview supported:"); | ||
|  | 		print_verbose("  max view count: " + itos(multiview_capabilities.max_view_count)); | ||
|  | 		//print_verbose("  max instances: " + itos(multiview_capabilities.max_instance_count)); // Hardcoded; not very useful at the moment.
 | ||
|  | 	} else { | ||
|  | 		print_verbose("- D3D12 multiview not supported"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (format_capabilities.relaxed_casting_supported) { | ||
|  | 		print_verbose("- Relaxed casting supported"); | ||
|  | 	} else { | ||
|  | 		print_verbose("- Relaxed casting not supported"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_initialize_debug_layers() { | ||
|  | 	ComPtr<ID3D12Debug> debug_controller; | ||
|  | 	HRESULT res = D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)); | ||
|  | 	ERR_FAIL_COND_V(res, ERR_QUERY_FAILED); | ||
|  | 	debug_controller->EnableDebugLayer(); | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_select_adapter(int &r_index) { | ||
|  | 	{ | ||
|  | 		UINT flags = _use_validation_layers() ? DXGI_CREATE_FACTORY_DEBUG : 0; | ||
|  | 		HRESULT res = CreateDXGIFactory2(flags, IID_PPV_ARGS(&dxgi_factory)); | ||
|  | 		ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ComPtr<IDXGIFactory6> factory6; | ||
|  | 	dxgi_factory.As(&factory6); | ||
|  | 
 | ||
|  | 	// TODO: Use IDXCoreAdapterList, which gives more comprehensive information.
 | ||
|  | 	LocalVector<IDXGIAdapter1 *> adapters; | ||
|  | 	while (true) { | ||
|  | 		IDXGIAdapter1 *curr_adapter = nullptr; | ||
|  | 		if (factory6) { | ||
|  | 			if (factory6->EnumAdapterByGpuPreference(adapters.size(), DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&curr_adapter)) == DXGI_ERROR_NOT_FOUND) { | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			if (dxgi_factory->EnumAdapters1(adapters.size(), &curr_adapter) == DXGI_ERROR_NOT_FOUND) { | ||
|  | 				break; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		adapters.push_back(curr_adapter); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ERR_FAIL_COND_V_MSG(adapters.size() == 0, ERR_CANT_CREATE, "Adapters enumeration reported zero accessible devices."); | ||
|  | 
 | ||
|  | 	// 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 adapter_index = -1; | ||
|  | 	int type_selected = -1; | ||
|  | 	LocalVector<RenderingDevice::DeviceType> adapter_types; | ||
|  | 	print_verbose("D3D12 devices:"); | ||
|  | 	for (uint32_t i = 0; i < adapters.size(); ++i) { | ||
|  | 		DXGI_ADAPTER_DESC1 desc = {}; | ||
|  | 		adapters[i]->GetDesc1(&desc); | ||
|  | 
 | ||
|  | 		String name = desc.Description; | ||
|  | 		String dev_type; | ||
|  | 		RenderingDevice::DeviceType type = {}; | ||
|  | 		if (((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE))) { | ||
|  | 			type = RenderingDevice::DEVICE_TYPE_CPU; | ||
|  | 		} else { | ||
|  | 			type = desc.DedicatedVideoMemory ? RenderingDevice::DEVICE_TYPE_DISCRETE_GPU : RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU; | ||
|  | 		} | ||
|  | 		adapter_types.push_back(type); | ||
|  | 
 | ||
|  | 		switch (type) { | ||
|  | 			case RenderingDevice::DEVICE_TYPE_DISCRETE_GPU: { | ||
|  | 				dev_type = "Discrete"; | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU: { | ||
|  | 				dev_type = "Integrated"; | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_VIRTUAL_GPU: { | ||
|  | 				dev_type = "Virtual"; | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_CPU: { | ||
|  | 				dev_type = "CPU"; | ||
|  | 			} break; | ||
|  | 			default: { | ||
|  | 				dev_type = "Other"; | ||
|  | 			} break; | ||
|  | 		} | ||
|  | 		print_verbose("  #" + itos(i) + ": " + name + ", " + dev_type); | ||
|  | 
 | ||
|  | 		switch (type) { | ||
|  | 			case RenderingDevice::DEVICE_TYPE_DISCRETE_GPU: { | ||
|  | 				if (type_selected < 4) { | ||
|  | 					type_selected = 4; | ||
|  | 					adapter_index = i; | ||
|  | 				} | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_INTEGRATED_GPU: { | ||
|  | 				if (type_selected < 3) { | ||
|  | 					type_selected = 3; | ||
|  | 					adapter_index = i; | ||
|  | 				} | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_VIRTUAL_GPU: { | ||
|  | 				if (type_selected < 2) { | ||
|  | 					type_selected = 2; | ||
|  | 					adapter_index = i; | ||
|  | 				} | ||
|  | 			} break; | ||
|  | 			case RenderingDevice::DEVICE_TYPE_CPU: { | ||
|  | 				if (type_selected < 1) { | ||
|  | 					type_selected = 1; | ||
|  | 					adapter_index = i; | ||
|  | 				} | ||
|  | 			} break; | ||
|  | 			default: { | ||
|  | 				if (type_selected < 0) { | ||
|  | 					type_selected = 0; | ||
|  | 					adapter_index = i; | ||
|  | 				} | ||
|  | 			} break; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	int32_t user_adapter_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU.
 | ||
|  | 	if (user_adapter_index >= 0 && user_adapter_index < (int32_t)adapters.size()) { | ||
|  | 		adapter_index = user_adapter_index; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ERR_FAIL_COND_V_MSG(adapter_index == -1, ERR_CANT_CREATE, "None of D3D12 devices supports hardware rendering."); | ||
|  | 
 | ||
|  | 	gpu = adapters[adapter_index]; | ||
|  | 	for (uint32_t i = 0; i < adapters.size(); ++i) { | ||
|  | 		adapters[i]->Release(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	adapter_type = adapter_types[adapter_index]; | ||
|  | 
 | ||
|  | 	ComPtr<IDXGIFactory5> factory5; | ||
|  | 	dxgi_factory.As(&factory5); | ||
|  | 	if (factory5) { | ||
|  | 		BOOL result = FALSE; // sizeof(bool) != sizeof(BOOL), in general.
 | ||
|  | 		HRESULT res = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &result, sizeof(result)); | ||
|  | 		if (SUCCEEDED(res)) { | ||
|  | 			tearing_supported = result; | ||
|  | 		} else { | ||
|  | 			ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	r_index = adapter_index; | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::_dump_adapter_info(int p_index) { | ||
|  | 	{ | ||
|  | 		const D3D_FEATURE_LEVEL FEATURE_LEVELS[] = { | ||
|  | 			D3D_FEATURE_LEVEL_11_0, | ||
|  | 			D3D_FEATURE_LEVEL_11_1, | ||
|  | 			D3D_FEATURE_LEVEL_12_0, | ||
|  | 			D3D_FEATURE_LEVEL_12_1, | ||
|  | 			D3D_FEATURE_LEVEL_12_2, | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		D3D12_FEATURE_DATA_FEATURE_LEVELS feat_levels = {}; | ||
|  | 		feat_levels.NumFeatureLevels = ARRAY_SIZE(FEATURE_LEVELS); | ||
|  | 		feat_levels.pFeatureLevelsRequested = FEATURE_LEVELS; | ||
|  | 
 | ||
|  | 		HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &feat_levels, sizeof(feat_levels)); | ||
|  | 		ERR_FAIL_COND_MSG(res, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 
 | ||
|  | 		// Example: D3D_FEATURE_LEVEL_12_1 = 0xc100.
 | ||
|  | 		uint32_t feat_level_major = feat_levels.MaxSupportedFeatureLevel >> 12; | ||
|  | 		uint32_t feat_level_minor = (feat_levels.MaxSupportedFeatureLevel >> 16) & 0xff; | ||
|  | 		feature_level = feat_level_major * 10 + feat_level_minor; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	String rendering_method; | ||
|  | 	if (OS::get_singleton()->get_current_rendering_method() == "mobile") { | ||
|  | 		rendering_method = "Forward Mobile"; | ||
|  | 	} else { | ||
|  | 		rendering_method = "Forward+"; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	static const struct { | ||
|  | 		uint32_t id; | ||
|  | 		const char *name; | ||
|  | 	} vendor_names[] = { | ||
|  | 		{ 0x1002, "AMD" }, | ||
|  | 		{ 0x1010, "ImgTec" }, | ||
|  | 		{ 0x106B, "Apple" }, | ||
|  | 		{ 0x10DE, "NVIDIA" }, | ||
|  | 		{ 0x13B5, "ARM" }, | ||
|  | 		{ 0x1414, "Microsoft" }, | ||
|  | 		{ 0x5143, "Qualcomm" }, | ||
|  | 		{ 0x8086, "Intel" }, | ||
|  | 		{ 0, nullptr }, | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	DXGI_ADAPTER_DESC gpu_desc = {}; | ||
|  | 	gpu->GetDesc(&gpu_desc); | ||
|  | 
 | ||
|  | 	adapter_name = gpu_desc.Description; | ||
|  | 	pipeline_cache_id = String::hex_encode_buffer((uint8_t *)&gpu_desc.AdapterLuid, sizeof(LUID)); | ||
|  | 	pipeline_cache_id += "-driver-" + itos(gpu_desc.Revision); | ||
|  | 	{ | ||
|  | 		adapter_vendor = "Unknown"; | ||
|  | 		uint32_t vendor_idx = 0; | ||
|  | 		while (vendor_names[vendor_idx].name != nullptr) { | ||
|  | 			if (gpu_desc.VendorId == vendor_names[vendor_idx].id) { | ||
|  | 				adapter_vendor = vendor_names[vendor_idx].name; | ||
|  | 				break; | ||
|  | 			} | ||
|  | 			vendor_idx++; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	print_line(vformat("D3D12 feature level %s - %s - Using D3D12 Adapter #%d: %s", get_device_api_version(), rendering_method, p_index, adapter_name)); | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_create_device(DeviceBasics &r_basics) { | ||
|  | 	HRESULT res = D3D12CreateDevice(gpu.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(r_basics.device.GetAddressOf())); | ||
|  | 	ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "D3D12CreateDevice failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 
 | ||
|  | 	// Create direct command queue.
 | ||
|  | 	D3D12_COMMAND_QUEUE_DESC queue_desc = {}; | ||
|  | 	queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; | ||
|  | 	queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; | ||
|  | 	res = r_basics.device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(r_basics.queue.GetAddressOf())); | ||
|  | 	ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	// Create sync objects.
 | ||
|  | 	res = r_basics.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(r_basics.fence.GetAddressOf())); | ||
|  | 	ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 	r_basics.fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); | ||
|  | 	ERR_FAIL_NULL_V(r_basics.fence_event, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	if (_use_validation_layers()) { | ||
|  | 		ComPtr<ID3D12InfoQueue> info_queue; | ||
|  | 		res = r_basics.device.As(&info_queue); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | #if 0 // This causes crashes. Needs investigation.
 | ||
|  | 		ComPtr<ID3D12InfoQueue1> info_queue_1; | ||
|  | 		device.As(&info_queue_1); | ||
|  | 		if (info_queue_1) { | ||
|  | 			// Custom printing supported (added in Windows 10 Release Preview build 20236).
 | ||
|  | 
 | ||
|  | 			info_queue_1->SetMuteDebugOutput(TRUE); | ||
|  | 
 | ||
|  | 			res = info_queue_1->RegisterMessageCallback(&_debug_message_func, D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS, nullptr, 0); | ||
|  | 			ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 		} else | ||
|  | #endif
 | ||
|  | 		{ | ||
|  | 			// Rely on D3D12's own debug printing.
 | ||
|  | 
 | ||
|  | 			if (Engine::get_singleton()->is_abort_on_gpu_errors_enabled()) { | ||
|  | 				res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); | ||
|  | 				ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		D3D12_MESSAGE_SEVERITY severities_to_mute[] = { | ||
|  | 			D3D12_MESSAGE_SEVERITY_INFO, | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		D3D12_MESSAGE_ID messages_to_mute[] = { | ||
|  | 			D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, | ||
|  | 			D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, | ||
|  | 			// These happen due to how D3D12MA manages buffers; seem bening.
 | ||
|  | 			D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE, | ||
|  | 			D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS, | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		D3D12_INFO_QUEUE_FILTER filter = {}; | ||
|  | 		filter.DenyList.NumSeverities = ARRAY_SIZE(severities_to_mute); | ||
|  | 		filter.DenyList.pSeverityList = severities_to_mute; | ||
|  | 		filter.DenyList.NumIDs = ARRAY_SIZE(messages_to_mute); | ||
|  | 		filter.DenyList.pIDList = messages_to_mute; | ||
|  | 
 | ||
|  | 		res = info_queue->PushStorageFilter(&filter); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_get_device_limits() { | ||
|  | 	D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; | ||
|  | 	HRESULT res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)); | ||
|  | 	ERR_FAIL_COND_V_MSG(res, ERR_UNAVAILABLE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 
 | ||
|  | 	// https://docs.microsoft.com/en-us/windows/win32/direct3d12/hardware-support
 | ||
|  | 	gpu_limits.max_srvs_per_shader_stage = options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1 ? 128 : UINT64_MAX; | ||
|  | 	gpu_limits.max_cbvs_per_shader_stage = options.ResourceBindingTier <= D3D12_RESOURCE_BINDING_TIER_2 ? 14 : UINT64_MAX; | ||
|  | 	gpu_limits.max_samplers_across_all_stages = options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1 ? 16 : 2048; | ||
|  | 	if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1) { | ||
|  | 		gpu_limits.max_uavs_across_all_stages = feature_level <= 110 ? 8 : 64; | ||
|  | 	} else if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_2) { | ||
|  | 		gpu_limits.max_uavs_across_all_stages = 64; | ||
|  | 	} else { | ||
|  | 		gpu_limits.max_uavs_across_all_stages = UINT64_MAX; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	md.queue->GetTimestampFrequency(&gpu_limits.timestamp_frequency); | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | bool D3D12Context::_use_validation_layers() { | ||
|  | 	return Engine::get_singleton()->is_validation_layers_enabled(); | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) { | ||
|  | 	ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); | ||
|  | 
 | ||
|  | 	Window window; | ||
|  | 	window.hwnd = p_window; | ||
|  | 	window.width = p_width; | ||
|  | 	window.height = p_height; | ||
|  | 	window.vsync_mode = p_vsync_mode; | ||
|  | 	Error err = _update_swap_chain(&window); | ||
|  | 	ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	windows[p_window_id] = window; | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::window_resize(DisplayServer::WindowID p_window, int p_width, int p_height) { | ||
|  | 	ERR_FAIL_COND(!windows.has(p_window)); | ||
|  | 	windows[p_window].width = p_width; | ||
|  | 	windows[p_window].height = p_height; | ||
|  | 	_update_swap_chain(&windows[p_window]); | ||
|  | } | ||
|  | 
 | ||
|  | int D3D12Context::window_get_width(DisplayServer::WindowID p_window) { | ||
|  | 	ERR_FAIL_COND_V(!windows.has(p_window), -1); | ||
|  | 	return windows[p_window].width; | ||
|  | } | ||
|  | 
 | ||
|  | int D3D12Context::window_get_height(DisplayServer::WindowID p_window) { | ||
|  | 	ERR_FAIL_COND_V(!windows.has(p_window), -1); | ||
|  | 	return windows[p_window].height; | ||
|  | } | ||
|  | 
 | ||
|  | bool D3D12Context::window_is_valid_swapchain(DisplayServer::WindowID p_window) { | ||
|  | 	ERR_FAIL_COND_V(!windows.has(p_window), false); | ||
|  | 	Window *w = &windows[p_window]; | ||
|  | 	return (bool)w->swapchain; | ||
|  | } | ||
|  | 
 | ||
|  | CD3DX12_CPU_DESCRIPTOR_HANDLE D3D12Context::window_get_framebuffer_rtv_handle(DisplayServer::WindowID p_window) { | ||
|  | 	ERR_FAIL_COND_V(!windows.has(p_window), CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT())); | ||
|  | 	ERR_FAIL_COND_V(!buffers_prepared, CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT())); | ||
|  | 	Window *w = &windows[p_window]; | ||
|  | 	CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle( | ||
|  | 			w->rtv_heap->GetCPUDescriptorHandleForHeapStart(), | ||
|  | 			w->current_buffer, | ||
|  | 			md.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); | ||
|  | 	return rtv_handle; | ||
|  | } | ||
|  | 
 | ||
|  | ID3D12Resource *D3D12Context::window_get_framebuffer_texture(DisplayServer::WindowID p_window) { | ||
|  | 	ERR_FAIL_COND_V(!windows.has(p_window), nullptr); | ||
|  | 	ERR_FAIL_COND_V(!buffers_prepared, nullptr); | ||
|  | 	Window *w = &windows[p_window]; | ||
|  | 	if (w->swapchain) { | ||
|  | 		return w->render_targets[w->current_buffer].Get(); | ||
|  | 	} else { | ||
|  | 		return nullptr; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::window_destroy(DisplayServer::WindowID p_window_id) { | ||
|  | 	ERR_FAIL_COND(!windows.has(p_window_id)); | ||
|  | 	_wait_for_idle_queue(md.queue.Get()); | ||
|  | 	windows.erase(p_window_id); | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::_update_swap_chain(Window *window) { | ||
|  | 	if (window->width == 0 || window->height == 0) { | ||
|  | 		// Likely window minimized, no swapchain created.
 | ||
|  | 		return ERR_SKIP; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	DisplayServer::VSyncMode curr_vsync_mode = window->vsync_mode; | ||
|  | 	bool vsync_mode_available = false; | ||
|  | 	UINT swapchain_flags = 0; | ||
|  | 	do { | ||
|  | 		switch (window->vsync_mode) { | ||
|  | 			case DisplayServer::VSYNC_MAILBOX: { | ||
|  | 				window->sync_interval = 1; | ||
|  | 				window->present_flags = DXGI_PRESENT_RESTART; | ||
|  | 				swapchain_flags = 0; | ||
|  | 				vsync_mode_available = true; | ||
|  | 			} break; | ||
|  | 			case DisplayServer::VSYNC_ADAPTIVE: { | ||
|  | 				vsync_mode_available = false; // I don't know how to set this up.
 | ||
|  | 			} break; | ||
|  | 			case DisplayServer::VSYNC_ENABLED: { | ||
|  | 				window->sync_interval = 1; | ||
|  | 				window->present_flags = 0; | ||
|  | 				swapchain_flags = 0; | ||
|  | 				vsync_mode_available = true; | ||
|  | 			} break; | ||
|  | 			case DisplayServer::VSYNC_DISABLED: { | ||
|  | 				window->sync_interval = 0; | ||
|  | 				window->present_flags = tearing_supported ? DXGI_PRESENT_ALLOW_TEARING : 0; | ||
|  | 				swapchain_flags = tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; | ||
|  | 				vsync_mode_available = true; | ||
|  | 			} break; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Set the windows swap effect if it is available, otherwise FLIP_DISCARD is used.
 | ||
|  | 		if (vsync_mode_available) { | ||
|  | 			if (window->vsync_mode != curr_vsync_mode || !window->swapchain) { | ||
|  | 				window->vsync_mode = curr_vsync_mode; | ||
|  | 				print_verbose("Using swapchain flags: " + itos(swapchain_flags) + ", sync interval: " + itos(window->sync_interval) + ", present flags: " + itos(window->present_flags)); | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			String present_mode_string; | ||
|  | 			switch (window->vsync_mode) { | ||
|  | 				case DisplayServer::VSYNC_MAILBOX: | ||
|  | 					present_mode_string = "Mailbox"; | ||
|  | 					break; | ||
|  | 				case DisplayServer::VSYNC_ADAPTIVE: | ||
|  | 					present_mode_string = "Adaptive"; | ||
|  | 					break; | ||
|  | 				case DisplayServer::VSYNC_ENABLED: | ||
|  | 					present_mode_string = "Enabled"; | ||
|  | 					break; | ||
|  | 				case DisplayServer::VSYNC_DISABLED: | ||
|  | 					present_mode_string = "Disabled"; | ||
|  | 					break; | ||
|  | 			} | ||
|  | 			WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_string)); | ||
|  | 			window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default.
 | ||
|  | 		} | ||
|  | 	} while (!vsync_mode_available); | ||
|  | 
 | ||
|  | 	if (window->swapchain) { | ||
|  | 		_wait_for_idle_queue(md.queue.Get()); | ||
|  | 		for (uint32_t i = 0; i < IMAGE_COUNT; i++) { | ||
|  | 			window->render_targets[i].Reset(); | ||
|  | 		} | ||
|  | 		window->rtv_heap.Reset(); | ||
|  | 
 | ||
|  | 		// D3D12 docs: "IDXGISwapChain::ResizeBuffers can't be used to add or remove this flag."
 | ||
|  | 		bool allow_tearing_flag_changed = (swapchain_flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) != (window->swapchain_flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); | ||
|  | 		if (allow_tearing_flag_changed) { | ||
|  | 			window->swapchain.Reset(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (!window->swapchain) { | ||
|  | 		DXGI_SWAP_CHAIN_DESC1 swapchain_desc = {}; | ||
|  | 		swapchain_desc.BufferCount = IMAGE_COUNT; | ||
|  | 		swapchain_desc.Width = 0; | ||
|  | 		swapchain_desc.Height = 0; | ||
|  | 		swapchain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | ||
|  | 		swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | ||
|  | 		swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; | ||
|  | 		swapchain_desc.SampleDesc.Count = 1; | ||
|  | 		swapchain_desc.Flags = swapchain_flags; | ||
|  | 		swapchain_desc.Scaling = DXGI_SCALING_NONE; | ||
|  | 
 | ||
|  | 		ComPtr<IDXGISwapChain1> swapchain; | ||
|  | 		HRESULT res = dxgi_factory->CreateSwapChainForHwnd(md.queue.Get(), window->hwnd, &swapchain_desc, nullptr, nullptr, swapchain.GetAddressOf()); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 		swapchain.As(&window->swapchain); | ||
|  | 		ERR_FAIL_NULL_V(window->swapchain, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 		format = swapchain_desc.Format; | ||
|  | 
 | ||
|  | 		res = dxgi_factory->MakeWindowAssociation(window->hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 		res = window->swapchain->GetDesc1(&swapchain_desc); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 		ERR_FAIL_COND_V(swapchain_desc.BufferCount != IMAGE_COUNT, ERR_BUG); | ||
|  | 		window->width = swapchain_desc.Width; | ||
|  | 		window->height = swapchain_desc.Height; | ||
|  | 
 | ||
|  | 	} else { | ||
|  | 		HRESULT res = window->swapchain->ResizeBuffers(IMAGE_COUNT, window->width, window->height, DXGI_FORMAT_UNKNOWN, swapchain_flags); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_UNAVAILABLE); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	window->swapchain_flags = swapchain_flags; | ||
|  | 	window->current_buffer = window->swapchain->GetCurrentBackBufferIndex(); | ||
|  | 
 | ||
|  | 	// Describe and create a render target view (RTV) descriptor heap.
 | ||
|  | 	D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = {}; | ||
|  | 	rtv_heap_desc.NumDescriptors = IMAGE_COUNT; | ||
|  | 	rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; | ||
|  | 	rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; | ||
|  | 	HRESULT res = md.device->CreateDescriptorHeap(&rtv_heap_desc, IID_PPV_ARGS(window->rtv_heap.GetAddressOf())); | ||
|  | 	ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	CD3DX12_CPU_DESCRIPTOR_HANDLE rtv_handle(window->rtv_heap->GetCPUDescriptorHandleForHeapStart()); | ||
|  | 
 | ||
|  | 	for (uint32_t i = 0; i < IMAGE_COUNT; i++) { | ||
|  | 		res = window->swapchain->GetBuffer(i, IID_PPV_ARGS(&window->render_targets[i])); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 		md.device->CreateRenderTargetView(window->render_targets[i].Get(), nullptr, rtv_handle); | ||
|  | 		rtv_handle.Offset(1, md.device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::initialize() { | ||
|  | 	if (_use_validation_layers()) { | ||
|  | 		Error err = _initialize_debug_layers(); | ||
|  | 		ERR_FAIL_COND_V(err, ERR_CANT_CREATE); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	int adapter_index = 0; | ||
|  | 
 | ||
|  | 	Error err = _select_adapter(adapter_index); | ||
|  | 	ERR_FAIL_COND_V(err, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	err = _create_device(md); | ||
|  | 	ERR_FAIL_COND_V(err, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	_dump_adapter_info(adapter_index); | ||
|  | 
 | ||
|  | 	err = _check_capabilities(); | ||
|  | 	ERR_FAIL_COND_V(err, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	err = _get_device_limits(); | ||
|  | 	ERR_FAIL_COND_V(err, ERR_CANT_CREATE); | ||
|  | 
 | ||
|  | 	{ | ||
|  | 		HRESULT res = md.device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(frame_fence.GetAddressOf())); | ||
|  | 		ERR_FAIL_COND_V(res, ERR_CANT_CREATE); | ||
|  | 		frame_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); | ||
|  | 		ERR_FAIL_NULL_V(frame_fence_event, ERR_CANT_CREATE); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	{ // Initialize allocator.
 | ||
|  | 		D3D12MA::ALLOCATOR_DESC allocator_desc = {}; | ||
|  | 		allocator_desc.pDevice = md.device.Get(); | ||
|  | 		allocator_desc.pAdapter = gpu.Get(); | ||
|  | 
 | ||
|  | 		HRESULT res = D3D12MA::CreateAllocator(&allocator_desc, &allocator); | ||
|  | 		ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "D3D12MA::CreateAllocator failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::set_setup_list(ID3D12CommandList *p_command_list) { | ||
|  | 	command_list_queue.write[0] = p_command_list; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::append_command_list(ID3D12CommandList *p_command_list) { | ||
|  | 	if (command_list_queue.size() <= command_list_count) { | ||
|  | 		command_list_queue.resize(command_list_count + 1); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	command_list_queue.write[command_list_count] = p_command_list; | ||
|  | 	command_list_count++; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::_wait_for_idle_queue(ID3D12CommandQueue *p_queue) { | ||
|  | 	md.fence_value++; | ||
|  | 	p_queue->Signal(md.fence.Get(), md.fence_value); | ||
|  | 	md.fence->SetEventOnCompletion(md.fence_value, md.fence_event); | ||
|  | 	WaitForSingleObjectEx(md.fence_event, INFINITE, FALSE); | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 	PIXNotifyWakeFromFenceSignal(md.fence_event); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::flush(bool p_flush_setup, bool p_flush_pending) { | ||
|  | 	if (p_flush_setup && command_list_queue[0]) { | ||
|  | 		md.queue->ExecuteCommandLists(1, command_list_queue.ptr()); | ||
|  | 		command_list_queue.write[0] = nullptr; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (p_flush_pending && command_list_count > 1) { | ||
|  | 		md.queue->ExecuteCommandLists(command_list_count - 1, command_list_queue.ptr() + 1); | ||
|  | 		command_list_count = 1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (p_flush_setup || p_flush_pending) { | ||
|  | 		_wait_for_idle_queue(md.queue.Get()); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::prepare_buffers(ID3D12GraphicsCommandList *p_command_list) { | ||
|  | 	// Ensure no more than FRAME_LAG renderings are outstanding.
 | ||
|  | 	if (frame >= IMAGE_COUNT) { | ||
|  | 		UINT64 min_value = frame - IMAGE_COUNT; | ||
|  | 		if (frame_fence->GetCompletedValue() < min_value) { | ||
|  | 			frame_fence->SetEventOnCompletion(min_value, frame_fence_event); | ||
|  | 			WaitForSingleObjectEx(frame_fence_event, INFINITE, FALSE); | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 			PIXNotifyWakeFromFenceSignal(frame_fence_event); | ||
|  | #endif
 | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	D3D12_RESOURCE_BARRIER *barriers = (D3D12_RESOURCE_BARRIER *)alloca(windows.size() * sizeof(D3D12_RESOURCE_BARRIER)); | ||
|  | 
 | ||
|  | 	uint32_t n = 0; | ||
|  | 	for (KeyValue<int, Window> &E : windows) { | ||
|  | 		Window *w = &E.value; | ||
|  | 		w->current_buffer = w->swapchain->GetCurrentBackBufferIndex(); | ||
|  | 		barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	p_command_list->ResourceBarrier(n, barriers); | ||
|  | 
 | ||
|  | 	buffers_prepared = true; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::postpare_buffers(ID3D12GraphicsCommandList *p_command_list) { | ||
|  | 	D3D12_RESOURCE_BARRIER *barriers = (D3D12_RESOURCE_BARRIER *)alloca(windows.size() * sizeof(D3D12_RESOURCE_BARRIER)); | ||
|  | 
 | ||
|  | 	uint32_t n = 0; | ||
|  | 	for (KeyValue<int, Window> &E : windows) { | ||
|  | 		Window *w = &E.value; | ||
|  | 		barriers[n++] = CD3DX12_RESOURCE_BARRIER::Transition(w->render_targets[w->current_buffer].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	p_command_list->ResourceBarrier(n, barriers); | ||
|  | } | ||
|  | 
 | ||
|  | Error D3D12Context::swap_buffers() { | ||
|  | 	ID3D12CommandList *const *commands_ptr = nullptr; | ||
|  | 	UINT commands_to_submit = 0; | ||
|  | 
 | ||
|  | 	if (command_list_queue[0] == nullptr) { | ||
|  | 		// No setup command, but commands to submit, submit from the first and skip command.
 | ||
|  | 		if (command_list_count > 1) { | ||
|  | 			commands_ptr = command_list_queue.ptr() + 1; | ||
|  | 			commands_to_submit = command_list_count - 1; | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		commands_ptr = command_list_queue.ptr(); | ||
|  | 		commands_to_submit = command_list_count; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	md.queue->ExecuteCommandLists(commands_to_submit, commands_ptr); | ||
|  | 
 | ||
|  | 	command_list_queue.write[0] = nullptr; | ||
|  | 	command_list_count = 1; | ||
|  | 
 | ||
|  | 	for (KeyValue<int, Window> &E : windows) { | ||
|  | 		Window *w = &E.value; | ||
|  | 
 | ||
|  | 		if (!w->swapchain) { | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 		HRESULT res = w->swapchain->Present(w->sync_interval, w->present_flags); | ||
|  | 		if (res) { | ||
|  | 			print_verbose("D3D12: Presenting swapchain of window " + itos(E.key) + " failed with error " + vformat("0x%08ux", res) + "."); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	md.queue->Signal(frame_fence.Get(), frame); | ||
|  | 	frame++; | ||
|  | 
 | ||
|  | 	buffers_prepared = false; | ||
|  | 	return OK; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::resize_notify() { | ||
|  | } | ||
|  | 
 | ||
|  | ComPtr<ID3D12Device> D3D12Context::get_device() { | ||
|  | 	return md.device; | ||
|  | } | ||
|  | 
 | ||
|  | ComPtr<IDXGIAdapter> D3D12Context::get_adapter() { | ||
|  | 	return gpu; | ||
|  | } | ||
|  | 
 | ||
|  | D3D12MA::Allocator *D3D12Context::get_allocator() { | ||
|  | 	return allocator.Get(); | ||
|  | } | ||
|  | 
 | ||
|  | int D3D12Context::get_swapchain_image_count() const { | ||
|  | 	return IMAGE_COUNT; | ||
|  | } | ||
|  | 
 | ||
|  | DXGI_FORMAT D3D12Context::get_screen_format() const { | ||
|  | 	return format; | ||
|  | } | ||
|  | 
 | ||
|  | D3D12Context::DeviceLimits D3D12Context::get_device_limits() const { | ||
|  | 	return gpu_limits; | ||
|  | } | ||
|  | 
 | ||
|  | RID D3D12Context::local_device_create() { | ||
|  | 	LocalDevice ld; | ||
|  | 	_create_device(ld); | ||
|  | 	return local_device_owner.make_rid(ld); | ||
|  | } | ||
|  | 
 | ||
|  | ComPtr<ID3D12Device> D3D12Context::local_device_get_d3d12_device(RID p_local_device) { | ||
|  | 	LocalDevice *ld = local_device_owner.get_or_null(p_local_device); | ||
|  | 	return ld->device; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::local_device_push_command_lists(RID p_local_device, ID3D12CommandList *const *p_lists, int p_count) { | ||
|  | 	LocalDevice *ld = local_device_owner.get_or_null(p_local_device); | ||
|  | 	ERR_FAIL_COND(ld->waiting); | ||
|  | 
 | ||
|  | 	ld->queue->ExecuteCommandLists(p_count, p_lists); | ||
|  | 
 | ||
|  | 	ld->waiting = true; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::local_device_sync(RID p_local_device) { | ||
|  | 	LocalDevice *ld = local_device_owner.get_or_null(p_local_device); | ||
|  | 	ERR_FAIL_COND(!ld->waiting); | ||
|  | 
 | ||
|  | 	ld->fence_value++; | ||
|  | 	ld->queue->Signal(ld->fence.Get(), ld->fence_value); | ||
|  | 	ld->fence->SetEventOnCompletion(ld->fence_value, ld->fence_event); | ||
|  | 	WaitForSingleObjectEx(ld->fence_event, INFINITE, FALSE); | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 	PIXNotifyWakeFromFenceSignal(ld->fence_event); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	ld->waiting = false; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::local_device_free(RID p_local_device) { | ||
|  | 	LocalDevice *ld = local_device_owner.get_or_null(p_local_device); | ||
|  | 
 | ||
|  | 	CloseHandle(ld->fence_event); | ||
|  | 
 | ||
|  | 	local_device_owner.free(p_local_device); | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::command_begin_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color) { | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 	PIXBeginEvent(p_command_list, p_color.to_argb32(), p_label_name.utf8().get_data()); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::command_insert_label(ID3D12GraphicsCommandList *p_command_list, String p_label_name, const Color p_color) { | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 	PIXSetMarker(p_command_list, p_color.to_argb32(), p_label_name.utf8().get_data()); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::command_end_label(ID3D12GraphicsCommandList *p_command_list) { | ||
|  | #ifdef PIX_ENABLED
 | ||
|  | 	PIXEndEvent(p_command_list); | ||
|  | #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::set_object_name(ID3D12Object *p_object, String p_object_name) { | ||
|  | 	ERR_FAIL_NULL(p_object); | ||
|  | 	int name_len = p_object_name.size(); | ||
|  | 	WCHAR *name_w = (WCHAR *)alloca(sizeof(WCHAR) * (name_len + 1)); | ||
|  | 	MultiByteToWideChar(CP_UTF8, 0, p_object_name.utf8().get_data(), -1, name_w, name_len); | ||
|  | 	p_object->SetName(name_w); | ||
|  | } | ||
|  | 
 | ||
|  | String D3D12Context::get_device_vendor_name() const { | ||
|  | 	return adapter_vendor; | ||
|  | } | ||
|  | String D3D12Context::get_device_name() const { | ||
|  | 	return adapter_name; | ||
|  | } | ||
|  | 
 | ||
|  | RenderingDevice::DeviceType D3D12Context::get_device_type() const { | ||
|  | 	return adapter_type; | ||
|  | } | ||
|  | 
 | ||
|  | String D3D12Context::get_device_api_version() const { | ||
|  | 	return vformat("%d_%d", feature_level / 10, feature_level % 10); | ||
|  | } | ||
|  | 
 | ||
|  | String D3D12Context::get_device_pipeline_cache_uuid() const { | ||
|  | 	return pipeline_cache_id; | ||
|  | } | ||
|  | 
 | ||
|  | DisplayServer::VSyncMode D3D12Context::get_vsync_mode(DisplayServer::WindowID p_window) const { | ||
|  | 	ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); | ||
|  | 	return windows[p_window].vsync_mode; | ||
|  | } | ||
|  | 
 | ||
|  | void D3D12Context::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { | ||
|  | 	ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); | ||
|  | 	windows[p_window].vsync_mode = p_mode; | ||
|  | 	_update_swap_chain(&windows[p_window]); | ||
|  | } | ||
|  | 
 | ||
|  | D3D12Context::D3D12Context() { | ||
|  | 	command_list_queue.resize(1); // First one is always the setup command.
 | ||
|  | 	command_list_queue.write[0] = nullptr; | ||
|  | 
 | ||
|  | 	strcpy(godot_nir_arch_name, Engine::get_singleton()->get_architecture_name().ascii().get_data()); | ||
|  | } | ||
|  | 
 | ||
|  | D3D12Context::~D3D12Context() { | ||
|  | 	if (md.fence_event) { | ||
|  | 		CloseHandle(md.fence_event); | ||
|  | 	} | ||
|  | 	if (frame_fence_event) { | ||
|  | 		CloseHandle(frame_fence_event); | ||
|  | 	} | ||
|  | } |