diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 329af9068e5..a90cf6e361a 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1470,6 +1470,8 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true); GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/frame_queue_size", PROPERTY_HINT_RANGE, "2,3,1"), 2); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/swapchain_image_count", PROPERTY_HINT_RANGE, "2,4,1"), 3); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index d76ac7c6d1a..86b45717828 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2698,6 +2698,20 @@ + + The number of frames to track on the CPU side before stalling to wait for the GPU. + Try the [url=https://darksylinc.github.io/vsync_simulator/]V-Sync Simulator[/url], an interactive interface that simulates presentation to better understand how it is affected by different variables under various conditions. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. + + + The number of images the swapchain will consist of (back buffers + front buffer). + [code]2[/code] corresponds to double-buffering and [code]3[/code] to triple-buffering. + Double-buffering may give you the lowest lag/latency but if V-Sync is on and the system can't render at 60 fps, the framerate will go down in multiples of it (e.g. 30 fps, 15, 7.5, etc.). Triple buffering gives you higher framerate (specially if the system can't reach a constant 60 fps) at the cost of up to 1 frame of latency, with [constant DisplayServer.VSYNC_ENABLED] (FIFO). + Use double-buffering with [constant DisplayServer.VSYNC_ENABLED]. Triple-buffering is a must if you plan on using [constant DisplayServer.VSYNC_MAILBOX] mode. + Try the [url=https://darksylinc.github.io/vsync_simulator/]V-Sync Simulator[/url], an interactive interface that simulates presentation to better understand how it is affected by different variables under various conditions. + [b]Note:[/b] This property is only read when the project starts. There is currently no way to change this value at run-time. + [b]Note:[/b] Some platforms may restrict the actual value. + diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 54f3281e17b..21b34db69df 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -590,8 +590,9 @@ + - Returns the screen's framebuffer format. + Returns the framebuffer format of the given screen. [b]Note:[/b] Only the main [RenderingDevice] returned by [method RenderingServer.get_rendering_device] has a format. If called on a local [RenderingDevice], this method prints an error and returns [constant INVALID_ID]. diff --git a/drivers/d3d12/d3d12_context.cpp b/drivers/d3d12/d3d12_context.cpp deleted file mode 100644 index d2d1cae5c87..00000000000 --- a/drivers/d3d12/d3d12_context.cpp +++ /dev/null @@ -1,1142 +0,0 @@ -/**************************************************************************/ -/* 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" - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wswitch" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - -#include "dxcapi.h" - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#if !defined(_MSC_VER) -#include - -#include -#endif - -// Note: symbol is not available in MinGW and old MSVC import libraries. -const CLSID CLSID_D3D12DeviceFactoryGodot = __uuidof(ID3D12DeviceFactory); -const CLSID CLSID_D3D12DebugGodot = __uuidof(ID3D12Debug); -const CLSID CLSID_D3D12SDKConfigurationGodot = __uuidof(ID3D12SDKConfiguration); - -extern "C" { -char godot_nir_arch_name[32]; -} - -#ifdef PIX_ENABLED -#if defined(__GNUC__) -#define _MSC_VER 1800 -#endif -#define USE_PIX -#include "WinPixEventRuntime/pix3.h" -#if defined(__GNUC__) -#undef _MSC_VER -#endif -#endif - -#define D3D12_DEBUG_LAYER_BREAK_ON_ERROR 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(!SUCCEEDED(res), ERR_CANT_CREATE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)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_OPTIONS4 options4 = {}; - res = md.device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4)); - if (SUCCEEDED(res)) { - shader_capabilities.native_16bit_ops = options4.Native16BitShaderOpsSupported; - } - print_verbose(String(" 16-bit ops supported: ") + (shader_capabilities.native_16bit_ops ? "yes" : "no")); - - 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 debug_controller; - HRESULT res; - if (device_factory) { - res = device_factory->GetConfigurationInterface(CLSID_D3D12DebugGodot, IID_PPV_ARGS(&debug_controller)); - } else { - res = D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)); - } - ERR_FAIL_COND_V(!SUCCEEDED(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 factory6; - dxgi_factory.As(&factory6); - - // TODO: Use IDXCoreAdapterList, which gives more comprehensive information. - LocalVector 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.is_empty(), 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 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 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", (uint64_t)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(!SUCCEEDED(res), "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)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; - if (device_factory) { - res = device_factory->CreateDevice(gpu.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(r_basics.device.GetAddressOf())); - } else { - res = D3D12CreateDevice(gpu.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(r_basics.device.GetAddressOf())); - } - ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "D3D12CreateDevice failed with error " + vformat("0x%08ux", (uint64_t)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(!SUCCEEDED(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(!SUCCEEDED(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 info_queue; - res = r_basics.device.As(&info_queue); - ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); - - ComPtr info_queue_1; - r_basics.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(!SUCCEEDED(res), ERR_CANT_CREATE); - } else { - // 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(!SUCCEEDED(res), ERR_CANT_CREATE); - res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); - ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); - res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); - ERR_FAIL_COND_V(!SUCCEEDED(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(!SUCCEEDED(res), ERR_CANT_CREATE); - -#if D3D12_DEBUG_LAYER_BREAK_ON_ERROR - res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); - ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); -#endif - } - - 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(!SUCCEEDED(res), ERR_UNAVAILABLE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)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, int p_width, int p_height, const void *p_platform_data) { - ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); - - Window window; - window.hwnd = ((const WindowPlatformData *)p_platform_data)->window; - window.width = p_width; - window.height = p_height; - window.vsync_mode = p_vsync_mode; - - { - RDD::Attachment attachment; - attachment.samples = RD::TEXTURE_SAMPLES_1; - attachment.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; - attachment.store_op = RDD::ATTACHMENT_STORE_OP_STORE; - window.render_pass.attachments.push_back(attachment); - - RDD::Subpass subpass; - { - RDD::AttachmentReference color_ref; - color_ref.attachment = 0; - color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); - subpass.color_references.push_back(color_ref); - } - window.render_pass.subpasses.push_back(subpass); - } - - for (uint32_t i = 0; i < IMAGE_COUNT; i++) { - Error err = window.framebuffers[i].rtv_heap.allocate(md.device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1, false); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - window.framebuffers[i].is_screen = true; - window.framebuffers[i].attachments_handle_inds.push_back(0); - } - - 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; -} - -RDD::RenderPassID D3D12Context::window_get_render_pass(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), RDD::RenderPassID()); - Window *w = &windows[p_window]; - return RDD::RenderPassID(&w->render_pass); -} - -RDD::FramebufferID D3D12Context::window_get_framebuffer(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), RDD::FramebufferID()); - ERR_FAIL_COND_V(!buffers_prepared, RDD::FramebufferID()); - Window *w = &windows[p_window]; - if (w->swapchain) { - return RDD::FramebufferID(&w->framebuffers[w->current_buffer]); - } else { - return RDD::FramebufferID(); - } -} - -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(); - } - - // 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 swapchain; - HRESULT res = dxgi_factory->CreateSwapChainForHwnd(md.queue.Get(), window->hwnd, &swapchain_desc, nullptr, nullptr, swapchain.GetAddressOf()); - ERR_FAIL_COND_V(!SUCCEEDED(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(!SUCCEEDED(res), ERR_CANT_CREATE); - - res = window->swapchain->GetDesc1(&swapchain_desc); - ERR_FAIL_COND_V(!SUCCEEDED(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(!SUCCEEDED(res), ERR_UNAVAILABLE); - } - - window->swapchain_flags = swapchain_flags; - window->current_buffer = window->swapchain->GetCurrentBackBufferIndex(); - - for (uint32_t i = 0; i < IMAGE_COUNT; i++) { - RenderingDeviceDriverD3D12::FramebufferInfo *fb_info = &window->framebuffers[i]; - RenderingDeviceDriverD3D12::DescriptorsHeap::Walker walker = fb_info->rtv_heap.make_walker(); - - HRESULT res = window->swapchain->GetBuffer(i, IID_PPV_ARGS(&window->render_targets[i])); - ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); - - md.device->CreateRenderTargetView(window->render_targets[i].Get(), nullptr, walker.get_curr_cpu_handle()); - } - - return OK; -} - -void D3D12Context::_init_device_factory() { - uint32_t agility_sdk_version = GLOBAL_GET("rendering/rendering_device/d3d12/agility_sdk_version"); - String agility_sdk_path = String(".\\") + Engine::get_singleton()->get_architecture_name(); - - // Note: symbol is not available in MinGW import library. - PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)GetProcAddress(LoadLibraryW(L"D3D12.dll"), "D3D12GetInterface"); - ERR_FAIL_COND(!d3d_D3D12GetInterface); - - ID3D12SDKConfiguration *sdk_config = nullptr; - if (SUCCEEDED(d3d_D3D12GetInterface(CLSID_D3D12SDKConfigurationGodot, IID_PPV_ARGS(&sdk_config)))) { - ID3D12SDKConfiguration1 *sdk_config1 = nullptr; - if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) { - if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf())))) { - d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf())); - } else if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf())))) { - d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf())); - } - sdk_config1->Release(); - } - sdk_config->Release(); - } -} - -Error D3D12Context::initialize() { - _init_device_factory(); - - 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(!SUCCEEDED(res), ERR_CANT_CREATE); - frame_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); - ERR_FAIL_NULL_V(frame_fence_event, ERR_CANT_CREATE); - } - - md.driver = memnew(RenderingDeviceDriverD3D12(this, md.device.Get(), IMAGE_COUNT + 1)); - - return OK; -} - -void D3D12Context::set_setup_buffer(RDD::CommandBufferID p_command_buffer) { - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - command_list_queue[0] = cmd_buf_info->cmd_list.Get(); -} - -void D3D12Context::append_command_buffer(RDD::CommandBufferID p_command_buffer) { - if (command_list_queue.size() <= command_list_count) { - command_list_queue.resize(command_list_count + 1); - } - - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - command_list_queue[command_list_count] = cmd_buf_info->cmd_list.Get(); - 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, bool p_sync) { - ERR_FAIL_COND_MSG(!p_sync, "Flush without sync is not supported."); // This is a special case for Vulkan on mobile XR hardware, not applicable to D3D12 - - if (p_flush_setup && command_list_queue[0]) { - md.queue->ExecuteCommandLists(1, command_list_queue.ptr()); - command_list_queue[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()); - } -} - -Error D3D12Context::prepare_buffers(RDD::CommandBufferID p_command_buffer) { - // 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 &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); - } - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - cmd_buf_info->cmd_list->ResourceBarrier(n, barriers); - - buffers_prepared = true; - - return OK; -} - -void D3D12Context::postpare_buffers(RDD::CommandBufferID p_command_buffer) { - D3D12_RESOURCE_BARRIER *barriers = (D3D12_RESOURCE_BARRIER *)alloca(windows.size() * sizeof(D3D12_RESOURCE_BARRIER)); - - uint32_t n = 0; - for (KeyValue &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); - } - - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - cmd_buf_info->cmd_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[0] = nullptr; - command_list_count = 1; - - for (KeyValue &E : windows) { - Window *w = &E.value; - - if (!w->swapchain) { - continue; - } - HRESULT res = w->swapchain->Present(w->sync_interval, w->present_flags); - if (!SUCCEEDED(res)) { - print_verbose("D3D12: Presenting swapchain of window " + itos(E.key) + " failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); - } - } - - md.queue->Signal(frame_fence.Get(), frame); - frame++; - - buffers_prepared = false; - return OK; -} - -void D3D12Context::resize_notify() { -} - -RenderingDevice::Capabilities D3D12Context::get_device_capabilities() const { - RenderingDevice::Capabilities c; - c.device_family = RenderingDevice::DEVICE_DIRECTX; - c.version_major = feature_level / 10; - c.version_minor = feature_level % 10; - return c; -} - -ID3D12Device *D3D12Context::get_device() { - return md.device.Get(); -} - -IDXGIAdapter *D3D12Context::get_adapter() { - return gpu.Get(); -} - -int D3D12Context::get_swapchain_image_count() const { - return IMAGE_COUNT; -} - -DXGI_FORMAT D3D12Context::get_screen_format() const { - return format; -} - -const D3D12Context::DeviceLimits &D3D12Context::get_device_limits() const { - return gpu_limits; -} - -RID D3D12Context::local_device_create() { - LocalDevice ld; - _create_device(ld); - ld.driver = memnew(RenderingDeviceDriverD3D12(this, ld.device.Get(), 1)); - return local_device_owner.make_rid(ld); -} - -void D3D12Context::local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - ERR_FAIL_COND(ld->waiting); - - ld->queue->ExecuteCommandLists(p_count, (ID3D12CommandList *const *)p_buffers); - - 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); - memdelete(ld->driver); - CloseHandle(ld->fence_event); - local_device_owner.free(p_local_device); -} - -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]); -} - -RenderingDeviceDriver *D3D12Context::get_driver(RID p_local_device) { - if (p_local_device.is_valid()) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - ERR_FAIL_NULL_V(ld, nullptr); - return ld->driver; - } else { - return md.driver; - } -} - -bool D3D12Context::is_debug_utils_enabled() const { -#ifdef PIX_ENABLED - return true; -#else - return false; -#endif -} - -D3D12Context::D3D12Context() { - command_list_queue.resize(1); // First one is always the setup command. - command_list_queue[0] = nullptr; - - CharString cs = Engine::get_singleton()->get_architecture_name().ascii(); - memcpy(godot_nir_arch_name, (const char *)cs.get_data(), cs.size()); -} - -D3D12Context::~D3D12Context() { - if (md.fence_event) { - CloseHandle(md.fence_event); - } - if (frame_fence_event) { - CloseHandle(frame_fence_event); - } -} diff --git a/drivers/d3d12/d3d12_context.h b/drivers/d3d12/d3d12_context.h deleted file mode 100644 index b6551d798db..00000000000 --- a/drivers/d3d12/d3d12_context.h +++ /dev/null @@ -1,261 +0,0 @@ -/**************************************************************************/ -/* d3d12_context.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. */ -/**************************************************************************/ - -#ifndef D3D12_CONTEXT_H -#define D3D12_CONTEXT_H - -#include "core/error/error_list.h" -#include "core/os/mutex.h" -#include "core/string/ustring.h" -#include "core/templates/rid_owner.h" -#include "rendering_device_driver_d3d12.h" -#include "servers/display_server.h" -#include "servers/rendering/renderer_rd/api_context_rd.h" - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wswitch" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#endif - -#if defined(AS) -#undef AS -#endif - -#include "d3dx12.h" -#include - -#include - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - -using Microsoft::WRL::ComPtr; - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - -class D3D12Context : public ApiContextRD { -public: - struct DeviceLimits { - uint64_t max_srvs_per_shader_stage; - uint64_t max_cbvs_per_shader_stage; - uint64_t max_samplers_across_all_stages; - uint64_t max_uavs_across_all_stages; - uint64_t timestamp_frequency; - }; - - struct SubgroupCapabilities { - uint32_t size; - bool wave_ops_supported; - uint32_t supported_stages_flags_rd() const; - uint32_t supported_operations_flags_rd() const; - }; - - struct VRSCapabilities { - bool draw_call_supported; // We can specify our fragment rate on a draw call level. - bool primitive_supported; // We can specify our fragment rate on each drawcall. - bool primitive_in_multiviewport; - bool ss_image_supported; // We can provide a density map attachment on our framebuffer. - uint32_t ss_image_tile_size; - bool additional_rates_supported; - }; - - struct ShaderCapabilities { - D3D_SHADER_MODEL shader_model; - bool native_16bit_ops; - }; - - struct StorageBufferCapabilities { - bool storage_buffer_16_bit_access_is_supported; - }; - - struct FormatCapabilities { - bool relaxed_casting_supported; - }; - -private: - enum { - FRAME_LAG = 2, - IMAGE_COUNT = FRAME_LAG + 1, - }; - - ComPtr device_factory; - ComPtr dxgi_factory; - ComPtr gpu; - DeviceLimits gpu_limits = {}; - struct DeviceBasics { - ComPtr device; - ComPtr queue; - ComPtr fence; - HANDLE fence_event = nullptr; - UINT64 fence_value = 0; - RenderingDeviceDriverD3D12 *driver = nullptr; - } md; // 'Main device', as opposed to local device. - - uint32_t feature_level = 0; // Major * 10 + minor. - bool tearing_supported = false; - SubgroupCapabilities subgroup_capabilities; - RDD::MultiviewCapabilities multiview_capabilities; - VRSCapabilities vrs_capabilities; - ShaderCapabilities shader_capabilities; - StorageBufferCapabilities storage_buffer_capabilities; - FormatCapabilities format_capabilities; - - String adapter_vendor; - String adapter_name; - RenderingDevice::DeviceType adapter_type = {}; - String pipeline_cache_id; - - bool buffers_prepared = false; - - DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; - uint32_t frame = 0; - ComPtr frame_fence; - HANDLE frame_fence_event = nullptr; - - struct Window { - HWND hwnd = nullptr; - ComPtr swapchain; - UINT swapchain_flags = 0; - UINT sync_interval = 1; - UINT present_flags = 0; - ComPtr render_targets[IMAGE_COUNT]; - uint32_t current_buffer = 0; - int width = 0; - int height = 0; - DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; - RenderingDeviceDriverD3D12::RenderPassInfo render_pass; - RenderingDeviceDriverD3D12::FramebufferInfo framebuffers[IMAGE_COUNT]; - }; - - struct LocalDevice : public DeviceBasics { - bool waiting = false; - HANDLE fence_event = nullptr; - UINT64 fence_value = 0; - }; - - RID_Owner local_device_owner; - - HashMap windows; - - // Commands. - - LocalVector command_list_queue; - uint32_t command_list_count = 1; - - static void STDMETHODCALLTYPE _debug_message_func( - D3D12_MESSAGE_CATEGORY p_category, - D3D12_MESSAGE_SEVERITY p_severity, - D3D12_MESSAGE_ID p_id, - LPCSTR p_description, - void *p_context); - - Error _initialize_debug_layers(); - void _init_device_factory(); - - Error _select_adapter(int &r_index); - void _dump_adapter_info(int p_index); - Error _create_device(DeviceBasics &r_basics); - Error _get_device_limits(); - Error _check_capabilities(); - - Error _update_swap_chain(Window *window); - - void _wait_for_idle_queue(ID3D12CommandQueue *p_queue); - -protected: - virtual bool _use_validation_layers(); - -public: - virtual const char *get_api_name() const override final { return "D3D12"; }; - virtual RenderingDevice::Capabilities get_device_capabilities() const override final; - const SubgroupCapabilities &get_subgroup_capabilities() const { return subgroup_capabilities; }; - virtual const RDD::MultiviewCapabilities &get_multiview_capabilities() const override final { return multiview_capabilities; }; - const VRSCapabilities &get_vrs_capabilities() const { return vrs_capabilities; }; - const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; }; - const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; - const FormatCapabilities &get_format_capabilities() const { return format_capabilities; }; - - ID3D12Device *get_device(); - IDXGIAdapter *get_adapter(); - virtual int get_swapchain_image_count() const override final; - - struct WindowPlatformData { - HWND window; - }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - virtual void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) override final; - virtual int window_get_width(DisplayServer::WindowID p_window = 0) override final; - virtual int window_get_height(DisplayServer::WindowID p_window = 0) override final; - virtual bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0) override final; - virtual void window_destroy(DisplayServer::WindowID p_window_id) override final; - virtual RDD::RenderPassID window_get_render_pass(DisplayServer::WindowID p_window = 0) override final; - virtual RDD::FramebufferID window_get_framebuffer(DisplayServer::WindowID p_window = 0) override final; - - virtual RID local_device_create() override final; - virtual void local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) override final; - virtual void local_device_sync(RID p_local_device) override final; - virtual void local_device_free(RID p_local_device) override final; - - DXGI_FORMAT get_screen_format() const; - const DeviceLimits &get_device_limits() const; - - virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) override final; - virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) override final; - void resize_notify(); - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) override final; - virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) override final; - virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) override final; - virtual Error swap_buffers() override final; - virtual Error initialize() override final; - - void set_object_name(ID3D12Object *p_object, String p_object_name); - - virtual String get_device_vendor_name() const override final; - virtual String get_device_name() const override final; - virtual RDD::DeviceType get_device_type() const override final; - virtual String get_device_api_version() const override final; - virtual String get_device_pipeline_cache_uuid() const override final; - - virtual void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) override final; - virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const override final; - - virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) override final; - virtual bool is_debug_utils_enabled() const override final; - - D3D12Context(); - virtual ~D3D12Context(); -}; - -#endif // D3D12_CONTEXT_H diff --git a/drivers/d3d12/d3d12ma.cpp b/drivers/d3d12/d3d12ma.cpp index 571ec952e75..ad7b4e570d9 100644 --- a/drivers/d3d12/d3d12ma.cpp +++ b/drivers/d3d12/d3d12ma.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "d3d12_context.h" +#include "rendering_context_driver_d3d12.h" #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp new file mode 100644 index 00000000000..ad3b793305e --- /dev/null +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -0,0 +1,313 @@ +/**************************************************************************/ +/* rendering_context_driver_d3d12.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 "rendering_context_driver_d3d12.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" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#include "dxcapi.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#if !defined(_MSC_VER) +#include + +#include +#endif + +// Note: symbols are not available in MinGW and old MSVC import libraries. +const CLSID CLSID_D3D12DeviceFactoryGodot = __uuidof(ID3D12DeviceFactory); +const CLSID CLSID_D3D12DebugGodot = __uuidof(ID3D12Debug); +const CLSID CLSID_D3D12SDKConfigurationGodot = __uuidof(ID3D12SDKConfiguration); + +extern "C" { +char godot_nir_arch_name[32]; +} + +#ifdef PIX_ENABLED +#if defined(__GNUC__) +#define _MSC_VER 1800 +#endif +#define USE_PIX +#include "WinPixEventRuntime/pix3.h" +#if defined(__GNUC__) +#undef _MSC_VER +#endif +#endif + +RenderingContextDriverD3D12::RenderingContextDriverD3D12() { + CharString cs = Engine::get_singleton()->get_architecture_name().ascii(); + memcpy(godot_nir_arch_name, (const char *)cs.get_data(), cs.size()); +} + +RenderingContextDriverD3D12::~RenderingContextDriverD3D12() { +} + +Error RenderingContextDriverD3D12::_init_device_factory() { + uint32_t agility_sdk_version = GLOBAL_GET("rendering/rendering_device/d3d12/agility_sdk_version"); + String agility_sdk_path = String(".\\") + Engine::get_singleton()->get_architecture_name(); + + // Note: symbol is not available in MinGW import library. + PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)GetProcAddress(LoadLibraryW(L"D3D12.dll"), "D3D12GetInterface"); + if (d3d_D3D12GetInterface == nullptr) { + // FIXME: Is it intended for this to silently return when it fails to find the symbol? + return OK; + } + + ID3D12SDKConfiguration *sdk_config = nullptr; + if (SUCCEEDED(d3d_D3D12GetInterface(CLSID_D3D12SDKConfigurationGodot, IID_PPV_ARGS(&sdk_config)))) { + ID3D12SDKConfiguration1 *sdk_config1 = nullptr; + if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) { + if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf())))) { + d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf())); + } else if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf())))) { + d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf())); + } + sdk_config1->Release(); + } + sdk_config->Release(); + } + + return OK; +} + +Error RenderingContextDriverD3D12::_initialize_debug_layers() { + ComPtr debug_controller; + HRESULT res; + if (device_factory) { + res = device_factory->GetConfigurationInterface(CLSID_D3D12DebugGodot, IID_PPV_ARGS(&debug_controller)); + } else { + res = D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller)); + } + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_QUERY_FAILED); + debug_controller->EnableDebugLayer(); + return OK; +} + +Error RenderingContextDriverD3D12::_initialize_devices() { + const UINT dxgi_factory_flags = use_validation_layers() ? DXGI_CREATE_FACTORY_DEBUG : 0; + HRESULT res = CreateDXGIFactory2(dxgi_factory_flags, IID_PPV_ARGS(&dxgi_factory)); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + // Enumerate all possible adapters. + LocalVector adapters; + IDXGIAdapter1 *adapter = nullptr; + do { + adapter = create_adapter(adapters.size()); + if (adapter != nullptr) { + adapters.push_back(adapter); + } + } while (adapter != nullptr); + + ERR_FAIL_COND_V_MSG(adapters.is_empty(), ERR_CANT_CREATE, "Adapters enumeration reported zero accessible devices."); + + // Fill the device descriptions with the adapters. + driver_devices.resize(adapters.size()); + for (uint32_t i = 0; i < adapters.size(); ++i) { + DXGI_ADAPTER_DESC1 desc = {}; + adapters[i]->GetDesc1(&desc); + + Device &device = driver_devices[i]; + device.name = desc.Description; + device.vendor = Vendor(desc.VendorId); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { + device.type = DEVICE_TYPE_CPU; + } else { + const bool has_dedicated_vram = desc.DedicatedVideoMemory > 0; + device.type = has_dedicated_vram ? DEVICE_TYPE_DISCRETE_GPU : DEVICE_TYPE_INTEGRATED_GPU; + } + } + + // Release all created adapters. + for (uint32_t i = 0; i < adapters.size(); ++i) { + adapters[i]->Release(); + } + + ComPtr factory_5; + dxgi_factory.As(&factory_5); + if (factory_5 != nullptr) { + // The type is important as in general, sizeof(bool) != sizeof(BOOL). + BOOL feature_supported = FALSE; + res = factory_5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &feature_supported, sizeof(feature_supported)); + if (SUCCEEDED(res)) { + tearing_supported = feature_supported; + } else { + ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + } + + return OK; +} + +bool RenderingContextDriverD3D12::use_validation_layers() const { + return Engine::get_singleton()->is_validation_layers_enabled(); +} + +Error RenderingContextDriverD3D12::initialize() { + Error err = _init_device_factory(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + if (use_validation_layers()) { + err = _initialize_debug_layers(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + + err = _initialize_devices(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + return OK; +} + +const RenderingContextDriver::Device &RenderingContextDriverD3D12::device_get(uint32_t p_device_index) const { + DEV_ASSERT(p_device_index < driver_devices.size()); + return driver_devices[p_device_index]; +} + +uint32_t RenderingContextDriverD3D12::device_get_count() const { + return driver_devices.size(); +} + +bool RenderingContextDriverD3D12::device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const { + // All devices should support presenting to any surface. + return true; +} + +RenderingDeviceDriver *RenderingContextDriverD3D12::driver_create() { + return memnew(RenderingDeviceDriverD3D12(this)); +} + +void RenderingContextDriverD3D12::driver_free(RenderingDeviceDriver *p_driver) { + memdelete(p_driver); +} + +RenderingContextDriver::SurfaceID RenderingContextDriverD3D12::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); + Surface *surface = memnew(Surface); + surface->hwnd = wpd->window; + return SurfaceID(surface); +} + +void RenderingContextDriverD3D12::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) { + Surface *surface = (Surface *)(p_surface); + surface->width = p_width; + surface->height = p_height; + surface->needs_resize = true; +} + +void RenderingContextDriverD3D12::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) { + Surface *surface = (Surface *)(p_surface); + surface->vsync_mode = p_vsync_mode; + surface->needs_resize = true; +} + +DisplayServer::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->vsync_mode; +} + +uint32_t RenderingContextDriverD3D12::surface_get_width(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->width; +} + +uint32_t RenderingContextDriverD3D12::surface_get_height(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->height; +} + +void RenderingContextDriverD3D12::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) { + Surface *surface = (Surface *)(p_surface); + surface->needs_resize = p_needs_resize; +} + +bool RenderingContextDriverD3D12::surface_get_needs_resize(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->needs_resize; +} + +void RenderingContextDriverD3D12::surface_destroy(SurfaceID p_surface) { + Surface *surface = (Surface *)(p_surface); + memdelete(surface); +} + +bool RenderingContextDriverD3D12::is_debug_utils_enabled() const { +#ifdef PIX_ENABLED + return true; +#else + return false; +#endif +} + +IDXGIAdapter1 *RenderingContextDriverD3D12::create_adapter(uint32_t p_adapter_index) const { + ComPtr factory_6; + dxgi_factory.As(&factory_6); + + // TODO: Use IDXCoreAdapterList, which gives more comprehensive information. + IDXGIAdapter1 *adapter = nullptr; + if (factory_6) { + if (factory_6->EnumAdapterByGpuPreference(p_adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) == DXGI_ERROR_NOT_FOUND) { + return nullptr; + } + } else { + if (dxgi_factory->EnumAdapters1(p_adapter_index, &adapter) == DXGI_ERROR_NOT_FOUND) { + return nullptr; + } + } + + return adapter; +} + +ID3D12DeviceFactory *RenderingContextDriverD3D12::device_factory_get() const { + return device_factory.Get(); +} + +IDXGIFactory2 *RenderingContextDriverD3D12::dxgi_factory_get() const { + return dxgi_factory.Get(); +} + +bool RenderingContextDriverD3D12::get_tearing_supported() const { + return tearing_supported; +} diff --git a/drivers/d3d12/rendering_context_driver_d3d12.h b/drivers/d3d12/rendering_context_driver_d3d12.h new file mode 100644 index 00000000000..694d0b3e4cc --- /dev/null +++ b/drivers/d3d12/rendering_context_driver_d3d12.h @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* rendering_context_driver_d3d12.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. */ +/**************************************************************************/ + +#ifndef RENDERING_CONTEXT_DRIVER_D3D12_H +#define RENDERING_CONTEXT_DRIVER_D3D12_H + +#include "core/error/error_list.h" +#include "core/os/mutex.h" +#include "core/string/ustring.h" +#include "core/templates/rid_owner.h" +#include "rendering_device_driver_d3d12.h" +#include "servers/display_server.h" +#include "servers/rendering/rendering_context_driver.h" + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + +#if defined(AS) +#undef AS +#endif + +#include "d3dx12.h" +#include + +#include + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + +using Microsoft::WRL::ComPtr; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +class RenderingContextDriverD3D12 : public RenderingContextDriver { + ComPtr device_factory; + ComPtr dxgi_factory; + TightLocalVector driver_devices; + bool tearing_supported = false; + + Error _init_device_factory(); + Error _initialize_debug_layers(); + Error _initialize_devices(); + +public: + virtual Error initialize() override; + virtual const Device &device_get(uint32_t p_device_index) const override; + virtual uint32_t device_get_count() const override; + virtual bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const override; + virtual RenderingDeviceDriver *driver_create() override; + virtual void driver_free(RenderingDeviceDriver *p_driver) override; + virtual SurfaceID surface_create(const void *p_platform_data) override; + virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override; + virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override; + virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override; + virtual uint32_t surface_get_width(SurfaceID p_surface) const override; + virtual uint32_t surface_get_height(SurfaceID p_surface) const override; + virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override; + virtual bool surface_get_needs_resize(SurfaceID p_surface) const override; + virtual void surface_destroy(SurfaceID p_surface) override; + virtual bool is_debug_utils_enabled() const override; + + // Platform-specific data for the Windows embedded in this driver. + struct WindowPlatformData { + HWND window; + }; + + // D3D12-only methods. + struct Surface { + HWND hwnd = NULL; + uint32_t width = 0; + uint32_t height = 0; + DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; + bool needs_resize = false; + }; + + IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const; + ID3D12DeviceFactory *device_factory_get() const; + IDXGIFactory2 *dxgi_factory_get() const; + bool get_tearing_supported() const; + bool use_validation_layers() const; + + RenderingContextDriverD3D12(); + virtual ~RenderingContextDriverD3D12() override; +}; + +#endif // RENDERING_CONTEXT_DRIVER_D3D12_H diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 23b697c8ed9..0db07177a51 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -32,10 +32,12 @@ #include "core/config/project_settings.h" #include "core/io/marshalls.h" -#include "d3d12_context.h" -#include "d3d12_godot_nir_bridge.h" +#include "servers/rendering/rendering_device.h" #include "thirdparty/zlib/zlib.h" +#include "d3d12_godot_nir_bridge.h" +#include "rendering_context_driver_d3d12.h" + // No point in fighting warnings in Mesa. #if defined(_MSC_VER) #pragma warning(push) @@ -96,6 +98,7 @@ static const uint32_t RUNTIME_DATA_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIE #ifdef DEV_ENABLED //#define DEBUG_COUNT_BARRIERS +#define CUSTOM_INFO_QUEUE_ENABLED 0 #endif /*****************/ @@ -390,6 +393,91 @@ static const D3D12_COMPARISON_FUNC RD_TO_D3D12_COMPARE_OP[RD::COMPARE_OP_MAX] = D3D12_COMPARISON_FUNC_ALWAYS, }; +uint32_t RenderingDeviceDriverD3D12::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 RenderingDeviceDriverD3D12::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); + } +} + +void RenderingDeviceDriverD3D12::_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; + } +} + /****************/ /**** MEMORY ****/ /****************/ @@ -444,7 +532,6 @@ static const D3D12_RESOURCE_DIMENSION RD_TEXTURE_TYPE_TO_D3D12_RESOURCE_DIMENSIO void RenderingDeviceDriverD3D12::_resource_transition_batch(ResourceInfo *p_resource, uint32_t p_subresource, uint32_t p_num_planes, D3D12_RESOURCE_STATES p_new_state, ID3D12Resource *p_resource_override) { DEV_ASSERT(p_subresource != UINT32_MAX); // We don't support an "all-resources" command here. - DEV_ASSERT(p_new_state != D3D12_RESOURCE_STATE_COMMON); // No need to support this for now. #ifdef DEBUG_COUNT_BARRIERS uint64_t start = OS::get_singleton()->get_ticks_usec(); @@ -455,7 +542,10 @@ void RenderingDeviceDriverD3D12::_resource_transition_batch(ResourceInfo *p_reso ID3D12Resource *res_to_transition = p_resource_override ? p_resource_override : p_resource->resource; - bool redundant_transition = ((*curr_state) & p_new_state) == p_new_state; + // Transitions can be considered redundant if the current state has all the bits of the new state. + // This check does not apply to the common state however, which must resort to checking if the state is the same (0). + bool any_state_is_common = *curr_state == D3D12_RESOURCE_STATE_COMMON || p_new_state == D3D12_RESOURCE_STATE_COMMON; + bool redundant_transition = any_state_is_common ? *curr_state == p_new_state : ((*curr_state) & p_new_state) == p_new_state; if (redundant_transition) { bool just_written = *curr_state == D3D12_RESOURCE_STATE_UNORDERED_ACCESS; bool needs_uav_barrier = just_written && res_states->last_batch_with_uav_barrier != res_barriers_batch; @@ -1048,7 +1138,7 @@ RDD::TextureID RenderingDeviceDriverD3D12::texture_create(const TextureFormat &p // 1. If ID3DDevice10 is present and driver reports relaxed casting is, leverage its new extended resource creation API (via D3D12MA). // 2. Otherwise, fall back to an approach based on abusing aliasing, hoping for the best. [[CROSS_FAMILY_ALIASING]] if (p_format.shareable_formats.size()) { - if (context->get_format_capabilities().relaxed_casting_supported) { + if (format_capabilities.relaxed_casting_supported) { ComPtr device_10; device->QueryInterface(device_10.GetAddressOf()); if (device_10) { @@ -1774,25 +1864,173 @@ void RenderingDeviceDriverD3D12::command_pipeline_barrier( } } -/*************************/ -/**** COMMAND BUFFERS ****/ -/*************************/ +/****************/ +/**** FENCES ****/ +/****************/ + +RDD::FenceID RenderingDeviceDriverD3D12::fence_create() { + ComPtr d3d_fence; + HRESULT res = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(d3d_fence.GetAddressOf())); + ERR_FAIL_COND_V(!SUCCEEDED(res), FenceID()); + + HANDLE event_handle = CreateEvent(nullptr, FALSE, FALSE, nullptr); + ERR_FAIL_NULL_V(event_handle, FenceID()); + + FenceInfo *fence = memnew(FenceInfo); + fence->d3d_fence = d3d_fence; + fence->event_handle = event_handle; + return FenceID(fence); +} + +Error RenderingDeviceDriverD3D12::fence_wait(FenceID p_fence) { + FenceInfo *fence = (FenceInfo *)(p_fence.id); + DWORD res = WaitForSingleObjectEx(fence->event_handle, INFINITE, FALSE); +#ifdef PIX_ENABLED + PIXNotifyWakeFromFenceSignal(fence->event_handle); +#endif + + return (res == WAIT_FAILED) ? FAILED : OK; +} + +void RenderingDeviceDriverD3D12::fence_free(FenceID p_fence) { + FenceInfo *fence = (FenceInfo *)(p_fence.id); + CloseHandle(fence->event_handle); + memdelete(fence); +} + +/********************/ +/**** SEMAPHORES ****/ +/********************/ + +RDD::SemaphoreID RenderingDeviceDriverD3D12::semaphore_create() { + ComPtr d3d_fence; + HRESULT res = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(d3d_fence.GetAddressOf())); + ERR_FAIL_COND_V(!SUCCEEDED(res), SemaphoreID()); + + SemaphoreInfo *semaphore = memnew(SemaphoreInfo); + semaphore->d3d_fence = d3d_fence; + return SemaphoreID(semaphore); +} + +void RenderingDeviceDriverD3D12::semaphore_free(SemaphoreID p_semaphore) { + SemaphoreInfo *semaphore = (SemaphoreInfo *)(p_semaphore.id); + memdelete(semaphore); +} + +/******************/ +/**** COMMANDS ****/ +/******************/ + +// ----- QUEUE FAMILY ----- + +RDD::CommandQueueFamilyID RenderingDeviceDriverD3D12::command_queue_family_get(BitField p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface) { + // Return the command list type encoded plus one so zero is an invalid value. + // The only ones that support presenting to a surface are direct queues. + if (p_cmd_queue_family_bits.has_flag(COMMAND_QUEUE_FAMILY_GRAPHICS_BIT) || (p_surface != 0)) { + return CommandQueueFamilyID(D3D12_COMMAND_LIST_TYPE_DIRECT + 1); + } else if (p_cmd_queue_family_bits.has_flag(COMMAND_QUEUE_FAMILY_COMPUTE_BIT)) { + return CommandQueueFamilyID(D3D12_COMMAND_LIST_TYPE_COMPUTE + 1); + } else if (p_cmd_queue_family_bits.has_flag(COMMAND_QUEUE_FAMILY_TRANSFER_BIT)) { + return CommandQueueFamilyID(D3D12_COMMAND_LIST_TYPE_COPY + 1); + } else { + return CommandQueueFamilyID(); + } +} + +// ----- QUEUE ----- + +RDD::CommandQueueID RenderingDeviceDriverD3D12::command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue) { + ComPtr d3d_queue; + D3D12_COMMAND_QUEUE_DESC queue_desc = {}; + queue_desc.Type = (D3D12_COMMAND_LIST_TYPE)(p_cmd_queue_family.id - 1); + HRESULT res = device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(d3d_queue.GetAddressOf())); + ERR_FAIL_COND_V(!SUCCEEDED(res), CommandQueueID()); + + CommandQueueInfo *command_queue = memnew(CommandQueueInfo); + command_queue->d3d_queue = d3d_queue; + return CommandQueueID(command_queue); +} + +Error RenderingDeviceDriverD3D12::command_queue_execute(CommandQueueID p_cmd_queue, VectorView p_cmd_buffers, VectorView p_wait_semaphores, VectorView p_signal_semaphores, FenceID p_signal_fence) { + CommandQueueInfo *command_queue = (CommandQueueInfo *)(p_cmd_queue.id); + for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { + const SemaphoreInfo *semaphore = (const SemaphoreInfo *)(p_wait_semaphores[i].id); + command_queue->d3d_queue->Wait(semaphore->d3d_fence.Get(), semaphore->fence_value); + } + + thread_local LocalVector command_lists; + command_lists.resize(p_cmd_buffers.size()); + for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)(p_cmd_buffers[i].id); + command_lists[i] = cmd_buf_info->cmd_list.Get(); + } + + command_queue->d3d_queue->ExecuteCommandLists(command_lists.size(), command_lists.ptr()); + + for (uint32_t i = 0; i < p_signal_semaphores.size(); i++) { + SemaphoreInfo *semaphore = (SemaphoreInfo *)(p_signal_semaphores[i].id); + semaphore->fence_value++; + command_queue->d3d_queue->Signal(semaphore->d3d_fence.Get(), semaphore->fence_value); + } + + if (p_signal_fence) { + FenceInfo *fence = (FenceInfo *)(p_signal_fence.id); + fence->fence_value++; + command_queue->d3d_queue->Signal(fence->d3d_fence.Get(), fence->fence_value); + fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle); + } + + return OK; +} + +Error RenderingDeviceDriverD3D12::command_queue_present(CommandQueueID p_cmd_queue, VectorView p_swap_chains, VectorView p_wait_semaphores) { + // D3D12 does not require waiting for the command queue's semaphores to handle presentation. + // We just present the swap chains that were specified and ignore the command queue and the semaphores. + HRESULT res; + bool any_present_failed = false; + for (uint32_t i = 0; i < p_swap_chains.size(); i++) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); + res = swap_chain->d3d_swap_chain->Present(swap_chain->sync_interval, swap_chain->present_flags); + if (!SUCCEEDED(res)) { + print_verbose("D3D12: Presenting swapchain failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + any_present_failed = true; + } + } + + return any_present_failed ? FAILED : OK; +} + +void RenderingDeviceDriverD3D12::command_queue_free(CommandQueueID p_cmd_queue) { + CommandQueueInfo *command_queue = (CommandQueueInfo *)(p_cmd_queue.id); + memdelete(command_queue); +} // ----- POOL ----- -RDD::CommandPoolID RenderingDeviceDriverD3D12::command_pool_create(CommandBufferType p_cmd_buffer_type) { - last_command_pool_id.id++; - return last_command_pool_id; +RDD::CommandPoolID RenderingDeviceDriverD3D12::command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) { + CommandPoolInfo *command_pool = memnew(CommandPoolInfo); + command_pool->queue_family = p_cmd_queue_family; + command_pool->buffer_type = p_cmd_buffer_type; + return CommandPoolID(command_pool); } void RenderingDeviceDriverD3D12::command_pool_free(CommandPoolID p_cmd_pool) { - pools_command_buffers.erase(p_cmd_pool); + CommandPoolInfo *command_pool = (CommandPoolInfo *)(p_cmd_pool.id); + memdelete(command_pool); } // ----- BUFFER ----- -RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) { - D3D12_COMMAND_LIST_TYPE list_type = p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY ? D3D12_COMMAND_LIST_TYPE_DIRECT : D3D12_COMMAND_LIST_TYPE_BUNDLE; +RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandPoolID p_cmd_pool) { + DEV_ASSERT(p_cmd_pool); + + const CommandPoolInfo *command_pool = (CommandPoolInfo *)(p_cmd_pool.id); + D3D12_COMMAND_LIST_TYPE list_type; + if (command_pool->buffer_type == COMMAND_BUFFER_TYPE_SECONDARY) { + list_type = D3D12_COMMAND_LIST_TYPE_BUNDLE; + } else { + list_type = D3D12_COMMAND_LIST_TYPE(command_pool->queue_family.id - 1); + } ID3D12CommandAllocator *cmd_allocator = nullptr; { @@ -1806,9 +2044,9 @@ RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandBu device->QueryInterface(device_4.GetAddressOf()); HRESULT res = E_FAIL; if (device_4) { - res = device_4->CreateCommandList1(0, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_FLAG_NONE, IID_PPV_ARGS(&cmd_list)); + res = device_4->CreateCommandList1(0, list_type, D3D12_COMMAND_LIST_FLAG_NONE, IID_PPV_ARGS(&cmd_list)); } else { - res = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmd_allocator, nullptr, IID_PPV_ARGS(&cmd_list)); + res = device->CreateCommandList(0, list_type, cmd_allocator, nullptr, IID_PPV_ARGS(&cmd_list)); } ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), CommandBufferID(), "CreateCommandList failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); if (!device_4) { @@ -1827,9 +2065,6 @@ RDD::CommandBufferID RenderingDeviceDriverD3D12::command_buffer_create(CommandBu bool RenderingDeviceDriverD3D12::command_buffer_begin(CommandBufferID p_cmd_buffer) { const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(cmd_buf_info->cmd_list->GetType() != D3D12_COMMAND_LIST_TYPE_DIRECT, false); -#endif HRESULT res = cmd_buf_info->cmd_allocator->Reset(); ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "Reset failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); res = cmd_buf_info->cmd_list->Reset(cmd_buf_info->cmd_allocator.Get(), nullptr); @@ -1839,9 +2074,6 @@ bool RenderingDeviceDriverD3D12::command_buffer_begin(CommandBufferID p_cmd_buff bool RenderingDeviceDriverD3D12::command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) { const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(cmd_buf_info->cmd_list->GetType() != D3D12_COMMAND_LIST_TYPE_BUNDLE, false); -#endif HRESULT res = cmd_buf_info->cmd_allocator->Reset(); ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), false, "Reset failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); res = cmd_buf_info->cmd_list->Reset(cmd_buf_info->cmd_allocator.Get(), nullptr); @@ -1858,22 +2090,219 @@ void RenderingDeviceDriverD3D12::command_buffer_end(CommandBufferID p_cmd_buffer cmd_buf_info->graphics_root_signature_crc = 0; cmd_buf_info->compute_pso = nullptr; cmd_buf_info->compute_root_signature_crc = 0; + cmd_buf_info->descriptor_heaps_set = false; } void RenderingDeviceDriverD3D12::command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView p_secondary_cmd_buffers) { const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(cmd_buf_info->cmd_list->GetType() != D3D12_COMMAND_LIST_TYPE_DIRECT); -#endif for (uint32_t i = 0; i < p_secondary_cmd_buffers.size(); i++) { const CommandBufferInfo *secondary_cb_info = (const CommandBufferInfo *)p_secondary_cmd_buffers[i].id; -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(secondary_cb_info->cmd_list->GetType() != D3D12_COMMAND_LIST_TYPE_BUNDLE); -#endif cmd_buf_info->cmd_list->ExecuteBundle(secondary_cb_info->cmd_list.Get()); } } +/********************/ +/**** SWAP CHAIN ****/ +/********************/ + +void RenderingDeviceDriverD3D12::_swap_chain_release(SwapChain *p_swap_chain) { + _swap_chain_release_buffers(p_swap_chain); + + p_swap_chain->d3d_swap_chain.Reset(); +} + +void RenderingDeviceDriverD3D12::_swap_chain_release_buffers(SwapChain *p_swap_chain) { + for (ID3D12Resource *render_target : p_swap_chain->render_targets) { + render_target->Release(); + } + + p_swap_chain->render_targets.clear(); + p_swap_chain->render_targets_info.clear(); + + for (RDD::FramebufferID framebuffer : p_swap_chain->framebuffers) { + framebuffer_free(framebuffer); + } + + p_swap_chain->framebuffers.clear(); +} + +RDD::SwapChainID RenderingDeviceDriverD3D12::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) { + // Create the render pass that will be used to draw to the swap chain's framebuffers. + RDD::Attachment attachment; + attachment.format = DATA_FORMAT_R8G8B8A8_UNORM; + attachment.samples = RDD::TEXTURE_SAMPLES_1; + attachment.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; + attachment.store_op = RDD::ATTACHMENT_STORE_OP_STORE; + + RDD::Subpass subpass; + RDD::AttachmentReference color_ref; + color_ref.attachment = 0; + color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); + subpass.color_references.push_back(color_ref); + + RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1); + ERR_FAIL_COND_V(!render_pass, SwapChainID()); + + // Create the empty swap chain until it is resized. + SwapChain *swap_chain = memnew(SwapChain); + swap_chain->surface = p_surface; + swap_chain->data_format = attachment.format; + swap_chain->render_pass = render_pass; + return SwapChainID(swap_chain); +} + +Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) { + DEV_ASSERT(p_cmd_queue.id != 0); + DEV_ASSERT(p_swap_chain.id != 0); + + CommandQueueInfo *command_queue = (CommandQueueInfo *)(p_cmd_queue.id); + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + RenderingContextDriverD3D12::Surface *surface = (RenderingContextDriverD3D12::Surface *)(swap_chain->surface); + if (surface->width == 0 || surface->height == 0) { + // Very likely the window is minimized, don't create a swap chain. + return ERR_SKIP; + } + + HRESULT res; + const bool is_tearing_supported = context_driver->get_tearing_supported(); + UINT sync_interval = 0; + UINT present_flags = 0; + UINT creation_flags = 0; + switch (surface->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: { + sync_interval = 1; + present_flags = DXGI_PRESENT_RESTART; + } break; + case DisplayServer::VSYNC_ENABLED: { + sync_interval = 1; + present_flags = 0; + } break; + case DisplayServer::VSYNC_DISABLED: { + sync_interval = 0; + present_flags = is_tearing_supported ? DXGI_PRESENT_ALLOW_TEARING : 0; + creation_flags = is_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; + } break; + case DisplayServer::VSYNC_ADAPTIVE: // Unsupported. + default: + sync_interval = 1; + present_flags = 0; + break; + } + + print_verbose("Using swap chain flags: " + itos(creation_flags) + ", sync interval: " + itos(sync_interval) + ", present flags: " + itos(present_flags)); + + if (swap_chain->d3d_swap_chain != nullptr && creation_flags != swap_chain->creation_flags) { + // The swap chain must be recreated if the creation flags are different. + _swap_chain_release(swap_chain); + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + if (swap_chain->d3d_swap_chain != nullptr) { + _swap_chain_release_buffers(swap_chain); + res = swap_chain->d3d_swap_chain->ResizeBuffers(p_desired_framebuffer_count, 0, 0, DXGI_FORMAT_UNKNOWN, creation_flags); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_UNAVAILABLE); + } else { + swap_chain_desc.BufferCount = p_desired_framebuffer_count; + swap_chain_desc.Format = RD_TO_D3D12_FORMAT[swap_chain->data_format].general_format; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.Flags = creation_flags; + swap_chain_desc.Scaling = DXGI_SCALING_NONE; + + ComPtr swap_chain_1; + res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + swap_chain_1.As(&swap_chain->d3d_swap_chain); + ERR_FAIL_NULL_V(swap_chain->d3d_swap_chain, ERR_CANT_CREATE); + + res = context_driver->dxgi_factory_get()->MakeWindowAssociation(surface->hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + } + + res = swap_chain->d3d_swap_chain->GetDesc1(&swap_chain_desc); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + ERR_FAIL_COND_V(swap_chain_desc.BufferCount == 0, ERR_CANT_CREATE); + + surface->width = swap_chain_desc.Width; + surface->height = swap_chain_desc.Height; + + swap_chain->creation_flags = creation_flags; + swap_chain->sync_interval = sync_interval; + swap_chain->present_flags = present_flags; + + // Retrieve the render targets associated to the swap chain and recreate the framebuffers. The following code + // relies on the address of the elements remaining static when new elements are inserted, so the container must + // follow this restriction when reserving the right amount of elements beforehand. + swap_chain->render_targets.reserve(swap_chain_desc.BufferCount); + swap_chain->render_targets_info.reserve(swap_chain_desc.BufferCount); + swap_chain->framebuffers.reserve(swap_chain_desc.BufferCount); + + for (uint32_t i = 0; i < swap_chain_desc.BufferCount; i++) { + // Retrieve the resource corresponding to the swap chain's buffer. + ID3D12Resource *render_target = nullptr; + res = swap_chain->d3d_swap_chain->GetBuffer(i, IID_PPV_ARGS(&render_target)); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + swap_chain->render_targets.push_back(render_target); + + // Create texture information for the framebuffer to reference the resource. Since the states pointer must + // reference an address of the element itself, we must insert it first and then modify it. + swap_chain->render_targets_info.push_back(TextureInfo()); + TextureInfo &texture_info = swap_chain->render_targets_info[i]; + texture_info.owner_info.states.subresource_states.push_back(D3D12_RESOURCE_STATE_PRESENT); + texture_info.states_ptr = &texture_info.owner_info.states; + texture_info.format = swap_chain->data_format; + texture_info.desc = CD3DX12_RESOURCE_DESC(render_target->GetDesc()); + texture_info.layers = 1; + texture_info.mipmaps = 1; + texture_info.resource = render_target; + texture_info.view_descs.srv.Format = texture_info.desc.Format; + texture_info.view_descs.srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + + // Create the framebuffer for this buffer. + FramebufferID framebuffer = _framebuffer_create(swap_chain->render_pass, TextureID(&swap_chain->render_targets_info[i]), swap_chain_desc.Width, swap_chain_desc.Height, true); + ERR_FAIL_COND_V(!framebuffer, ERR_CANT_CREATE); + swap_chain->framebuffers.push_back(framebuffer); + } + + // Once everything's been created correctly, indicate the surface no longer needs to be resized. + context_driver->surface_set_needs_resize(swap_chain->surface, false); + + return OK; +} + +RDD::FramebufferID RenderingDeviceDriverD3D12::swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) { + DEV_ASSERT(p_swap_chain.id != 0); + + const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id); + if (context_driver->surface_get_needs_resize(swap_chain->surface)) { + r_resize_required = true; + return FramebufferID(); + } + + const uint32_t buffer_index = swap_chain->d3d_swap_chain->GetCurrentBackBufferIndex(); + DEV_ASSERT(buffer_index < swap_chain->framebuffers.size()); + return swap_chain->framebuffers[buffer_index]; +} + +RDD::RenderPassID RenderingDeviceDriverD3D12::swap_chain_get_render_pass(SwapChainID p_swap_chain) { + const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id); + return swap_chain->render_pass; +} + +RDD::DataFormat RenderingDeviceDriverD3D12::swap_chain_get_format(SwapChainID p_swap_chain) { + const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id); + return swap_chain->data_format; +} + +void RenderingDeviceDriverD3D12::swap_chain_free(SwapChainID p_swap_chain) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + _swap_chain_release(swap_chain); + render_pass_free(swap_chain->render_pass); + memdelete(swap_chain); +} + /*********************/ /**** FRAMEBUFFER ****/ /*********************/ @@ -1970,9 +2399,10 @@ D3D12_DEPTH_STENCIL_VIEW_DESC RenderingDeviceDriverD3D12::_make_dsv_for_texture( return dsv_desc; } -RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p_render_pass, VectorView p_attachments, uint32_t p_width, uint32_t p_height) { +RDD::FramebufferID RenderingDeviceDriverD3D12::_framebuffer_create(RenderPassID p_render_pass, VectorView p_attachments, uint32_t p_width, uint32_t p_height, bool p_is_screen) { // Pre-bookkeep. FramebufferInfo *fb_info = VersatileResource::allocate(resources_allocator); + fb_info->is_screen = p_is_screen; const RenderPassInfo *pass_info = (const RenderPassInfo *)p_render_pass.id; @@ -1995,7 +2425,7 @@ RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p } if (num_color) { - Error err = fb_info->rtv_heap.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, num_color, false); + Error err = fb_info->rtv_heap.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, num_color, false); if (err) { VersatileResource::free(resources_allocator, fb_info); ERR_FAIL_V(FramebufferID()); @@ -2004,7 +2434,7 @@ RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p DescriptorsHeap::Walker rtv_heap_walker = fb_info->rtv_heap.make_walker(); if (num_depth_stencil) { - Error err = fb_info->dsv_heap.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, num_depth_stencil, false); + Error err = fb_info->dsv_heap.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, num_depth_stencil, false); if (err) { VersatileResource::free(resources_allocator, fb_info); ERR_FAIL_V(FramebufferID()); @@ -2056,6 +2486,10 @@ RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p return FramebufferID(fb_info); } +RDD::FramebufferID RenderingDeviceDriverD3D12::framebuffer_create(RenderPassID p_render_pass, VectorView p_attachments, uint32_t p_width, uint32_t p_height) { + return _framebuffer_create(p_render_pass, p_attachments, p_width, p_height, false); +} + void RenderingDeviceDriverD3D12::framebuffer_free(FramebufferID p_framebuffer) { FramebufferInfo *fb_info = (FramebufferInfo *)p_framebuffer.id; VersatileResource::free(resources_allocator, fb_info); @@ -2239,7 +2673,7 @@ bool RenderingDeviceDriverD3D12::_shader_sign_dxil_bytecode(ShaderStage p_stage, } String RenderingDeviceDriverD3D12::shader_get_binary_cache_key() { - return "D3D12-SV" + uitos(ShaderBinary::VERSION) + "-" + itos(context->get_shader_capabilities().shader_model); + return "D3D12-SV" + uitos(ShaderBinary::VERSION) + "-" + itos(shader_capabilities.shader_model); } Vector RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(VectorView p_spirv, const String &p_shader_name) { @@ -2509,7 +2943,7 @@ Vector RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec nir_to_dxil_options nir_to_dxil_options = {}; nir_to_dxil_options.environment = DXIL_ENVIRONMENT_VULKAN; - nir_to_dxil_options.shader_model_max = shader_model_d3d_to_dxil(context->get_shader_capabilities().shader_model); + nir_to_dxil_options.shader_model_max = shader_model_d3d_to_dxil(shader_capabilities.shader_model); nir_to_dxil_options.validator_version_max = dxil_get_validator_version(_get_dxil_validator_for_current_thread()); nir_to_dxil_options.godot_nir_callbacks = &godot_nir_callbacks; @@ -3105,14 +3539,14 @@ RDD::UniformSetID RenderingDeviceDriverD3D12::uniform_set_create(VectorViewdesc_heaps.resources.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, num_resource_descs, false); + Error err = uniform_set_info->desc_heaps.resources.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, num_resource_descs, false); if (err) { VersatileResource::free(resources_allocator, uniform_set_info); ERR_FAIL_V(UniformSetID()); } } if (num_sampler_descs) { - Error err = uniform_set_info->desc_heaps.samplers.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_sampler_descs, false); + Error err = uniform_set_info->desc_heaps.samplers.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_sampler_descs, false); if (err) { VersatileResource::free(resources_allocator, uniform_set_info); ERR_FAIL_V(UniformSetID()); @@ -3481,11 +3915,24 @@ void RenderingDeviceDriverD3D12::command_uniform_set_prepare_for_use(CommandBuff } } -void RenderingDeviceDriverD3D12::_command_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute) { - if (!unlikely(segment_begun)) { - // Support out-of-frame rendering, like the boot splash screen. - begin_segment(p_cmd_buffer, frame_idx, frames_drawn); +void RenderingDeviceDriverD3D12::_command_check_descriptor_sets(CommandBufferID p_cmd_buffer) { + DEV_ASSERT(segment_begun && "Unable to use commands that rely on descriptors because a segment was never begun."); + + CommandBufferInfo *cmd_buf_info = (CommandBufferInfo *)p_cmd_buffer.id; + if (!cmd_buf_info->descriptor_heaps_set) { + // Set descriptor heaps for the command buffer if they haven't been set yet. + ID3D12DescriptorHeap *heaps[] = { + frames[frame_idx].desc_heaps.resources.get_heap(), + frames[frame_idx].desc_heaps.samplers.get_heap(), + }; + + cmd_buf_info->cmd_list->SetDescriptorHeaps(2, heaps); + cmd_buf_info->descriptor_heaps_set = true; } +} + +void RenderingDeviceDriverD3D12::_command_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute) { + _command_check_descriptor_sets(p_cmd_buffer); UniformSetInfo *uniform_set_info = (UniformSetInfo *)p_uniform_set.id; const ShaderInfo *shader_info_in = (const ShaderInfo *)p_shader.id; @@ -3715,6 +4162,8 @@ void RenderingDeviceDriverD3D12::_command_bind_uniform_set(CommandBufferID p_cmd /******************/ void RenderingDeviceDriverD3D12::command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) { + _command_check_descriptor_sets(p_cmd_buffer); + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; BufferInfo *buf_info = (BufferInfo *)p_buffer.id; @@ -3897,6 +4346,7 @@ void RenderingDeviceDriverD3D12::command_clear_color_texture(CommandBufferID p_c frames[frame_idx].desc_heap_walkers.rtv.advance(); } else { // Clear via UAV. + _command_check_descriptor_sets(p_cmd_buffer); if (frames[frame_idx].desc_heap_walkers.resources.is_at_eof()) { if (!frames[frame_idx].desc_heaps_exhausted_reported.resources) { @@ -4202,7 +4652,6 @@ void RenderingDeviceDriverD3D12::command_begin_render_pass(CommandBufferID p_cmd } }; - // This is empty if a screen framebuffer. Transition in that case happens in D3D12Context::prepare_buffers(). for (uint32_t i = 0; i < fb_info->attachments.size(); i++) { TextureInfo *tex_info = (TextureInfo *)fb_info->attachments[i].id; if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { @@ -4231,16 +4680,14 @@ void RenderingDeviceDriverD3D12::command_begin_render_pass(CommandBufferID p_cmd cmd_buf_info->render_pass_state.region_rect.right == fb_info->size.x && cmd_buf_info->render_pass_state.region_rect.bottom == fb_info->size.y); - if (fb_info->is_screen) { - for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { - if (pass_info->attachments[i].load_op == ATTACHMENT_LOAD_OP_DONT_CARE) { - const TextureInfo *tex_info = (const TextureInfo *)fb_info->attachments[i].id; - _discard_texture_subresources(tex_info, cmd_buf_info); - } + for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { + if (pass_info->attachments[i].load_op == ATTACHMENT_LOAD_OP_DONT_CARE) { + const TextureInfo *tex_info = (const TextureInfo *)fb_info->attachments[i].id; + _discard_texture_subresources(tex_info, cmd_buf_info); } } - if (fb_info->vrs_attachment && context->get_vrs_capabilities().ss_image_supported) { + if (fb_info->vrs_attachment && vrs_capabilities.ss_image_supported) { ComPtr cmd_list_5; cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf()); if (cmd_list_5) { @@ -4257,41 +4704,33 @@ void RenderingDeviceDriverD3D12::command_begin_render_pass(CommandBufferID p_cmd cmd_buf_info->render_pass_state.pass_info = pass_info; command_next_render_subpass(p_cmd_buffer, p_cmd_buffer_type); - AttachmentClear *clears = ALLOCA_ARRAY(AttachmentClear, fb_info->is_screen ? 1 : pass_info->attachments.size()); - Rect2i *clear_rects = ALLOCA_ARRAY(Rect2i, fb_info->is_screen ? 1 : pass_info->attachments.size()); + AttachmentClear *clears = ALLOCA_ARRAY(AttachmentClear, pass_info->attachments.size()); + Rect2i *clear_rects = ALLOCA_ARRAY(Rect2i, pass_info->attachments.size()); uint32_t num_clears = 0; - if (fb_info->is_screen) { - clears[0].aspect.set_flag(TEXTURE_ASPECT_COLOR_BIT); - clears[0].color_attachment = 0; - clears[0].value = p_attachment_clears[0]; - clear_rects[0] = p_rect; - num_clears++; - } else { - for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { - TextureInfo *tex_info = (TextureInfo *)fb_info->attachments[i].id; - if (!tex_info) { - continue; - } + for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { + TextureInfo *tex_info = (TextureInfo *)fb_info->attachments[i].id; + if (!tex_info) { + continue; + } - AttachmentClear clear; - if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { - if (pass_info->attachments[i].load_op == ATTACHMENT_LOAD_OP_CLEAR) { - clear.aspect.set_flag(TEXTURE_ASPECT_COLOR_BIT); - clear.color_attachment = i; - } - } else if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { - if (pass_info->attachments[i].stencil_load_op == ATTACHMENT_LOAD_OP_CLEAR) { - clear.aspect.set_flag(TEXTURE_ASPECT_DEPTH_BIT); - } + AttachmentClear clear; + if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + if (pass_info->attachments[i].load_op == ATTACHMENT_LOAD_OP_CLEAR) { + clear.aspect.set_flag(TEXTURE_ASPECT_COLOR_BIT); + clear.color_attachment = i; } - if (!clear.aspect.is_empty()) { - clear.value = p_attachment_clears[i]; - clears[num_clears] = clear; - clear_rects[num_clears] = p_rect; - num_clears++; + } else if ((tex_info->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) { + if (pass_info->attachments[i].stencil_load_op == ATTACHMENT_LOAD_OP_CLEAR) { + clear.aspect.set_flag(TEXTURE_ASPECT_DEPTH_BIT); } } + if (!clear.aspect.is_empty()) { + clear.value = p_attachment_clears[i]; + clears[num_clears] = clear; + clear_rects[num_clears] = p_rect; + num_clears++; + } } if (num_clears) { @@ -4308,6 +4747,15 @@ void RenderingDeviceDriverD3D12::_end_render_pass(CommandBufferID p_cmd_buffer) const RenderPassInfo *pass_info = cmd_buf_info->render_pass_state.pass_info; const Subpass &subpass = pass_info->subpasses[cmd_buf_info->render_pass_state.current_subpass]; + if (fb_info->is_screen) { + // Screen framebuffers must transition back to present state when the render pass is finished. + for (uint32_t i = 0; i < fb_info->attachments.size(); i++) { + TextureInfo *src_tex_info = (TextureInfo *)(fb_info->attachments[i].id); + uint32_t src_subresource = D3D12CalcSubresource(src_tex_info->base_mip, src_tex_info->base_layer, 0, src_tex_info->desc.MipLevels, src_tex_info->desc.ArraySize()); + _resource_transition_batch(src_tex_info, src_subresource, 1, D3D12_RESOURCE_STATE_PRESENT); + } + } + struct Resolve { ID3D12Resource *src_res = nullptr; uint32_t src_subres = 0; @@ -4358,7 +4806,7 @@ void RenderingDeviceDriverD3D12::command_end_render_pass(CommandBufferID p_cmd_b const FramebufferInfo *fb_info = cmd_buf_info->render_pass_state.fb_info; const RenderPassInfo *pass_info = cmd_buf_info->render_pass_state.pass_info; - if (context->get_vrs_capabilities().ss_image_supported) { + if (vrs_capabilities.ss_image_supported) { ComPtr cmd_list_5; cmd_buf_info->cmd_list->QueryInterface(cmd_list_5.GetAddressOf()); if (cmd_list_5) { @@ -4366,12 +4814,10 @@ void RenderingDeviceDriverD3D12::command_end_render_pass(CommandBufferID p_cmd_b } } - if (fb_info->attachments.size()) { // Otherwise, it's screen. - for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { - if (pass_info->attachments[i].store_op == ATTACHMENT_STORE_OP_DONT_CARE) { - const TextureInfo *tex_info = (const TextureInfo *)fb_info->attachments[i].id; - _discard_texture_subresources(tex_info, cmd_buf_info); - } + for (uint32_t i = 0; i < pass_info->attachments.size(); i++) { + if (pass_info->attachments[i].store_op == ATTACHMENT_STORE_OP_DONT_CARE) { + const TextureInfo *tex_info = (const TextureInfo *)fb_info->attachments[i].id; + _discard_texture_subresources(tex_info, cmd_buf_info); } } @@ -5163,7 +5609,7 @@ void RenderingDeviceDriverD3D12::timestamp_query_pool_get_results(QueryPoolID p_ } uint64_t RenderingDeviceDriverD3D12::timestamp_query_result_to_time(uint64_t p_result) { - return p_result / (double)context->get_device_limits().timestamp_frequency * 1000000000.0; + return p_result / (double)device_limits.timestamp_frequency * 1000000000.0; } void RenderingDeviceDriverD3D12::command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) { @@ -5191,29 +5637,11 @@ void RenderingDeviceDriverD3D12::command_end_label(CommandBufferID p_cmd_buffer) #endif } -/****************/ -/**** SCREEN ****/ -/****************/ - -RDD::DataFormat RenderingDeviceDriverD3D12::screen_get_format() { - // Very hacky, but not used often per frame, so I guess ok. - DXGI_FORMAT d3d12_format = context->get_screen_format(); - DataFormat format = DATA_FORMAT_MAX; - for (int i = 0; i < DATA_FORMAT_MAX; i++) { - if (d3d12_format == RD_TO_D3D12_FORMAT[i].general_format) { - format = DataFormat(i); - break; - } - } - ERR_FAIL_COND_V(format == DATA_FORMAT_MAX, DATA_FORMAT_MAX); - return format; -} - /********************/ /**** SUBMISSION ****/ /********************/ -void RenderingDeviceDriverD3D12::begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) { +void RenderingDeviceDriverD3D12::begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) { frame_idx = p_frame_index; frames_drawn = p_frames_drawn; @@ -5224,17 +5652,9 @@ void RenderingDeviceDriverD3D12::begin_segment(CommandBufferID p_cmd_buffer, uin frames[frame_idx].desc_heap_walkers.aux.rewind(); frames[frame_idx].desc_heap_walkers.rtv.rewind(); frames[frame_idx].desc_heaps_exhausted_reported = {}; - frames[frame_idx].null_rtv_handle = { 0 }; + frames[frame_idx].null_rtv_handle = {}; frames[frame_idx].segment_serial = segment_serial; - ID3D12DescriptorHeap *heaps[] = { - frames[frame_idx].desc_heaps.resources.get_heap(), - frames[frame_idx].desc_heaps.samplers.get_heap(), - }; - - const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; - cmd_buf_info->cmd_list->SetDescriptorHeaps(2, heaps); - segment_begun = true; } @@ -5247,36 +5667,44 @@ void RenderingDeviceDriverD3D12::end_segment() { /**** MISC ****/ /**************/ +void RenderingDeviceDriverD3D12::_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); +} + void RenderingDeviceDriverD3D12::set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) { switch (p_type) { case OBJECT_TYPE_TEXTURE: { const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; if (tex_info->owner_info.allocation) { - context->set_object_name(tex_info->resource, p_name); + _set_object_name(tex_info->resource, p_name); } } break; case OBJECT_TYPE_SAMPLER: { } break; case OBJECT_TYPE_BUFFER: { const BufferInfo *buf_info = (const BufferInfo *)p_driver_id.id; - context->set_object_name(buf_info->resource, p_name); + _set_object_name(buf_info->resource, p_name); } break; case OBJECT_TYPE_SHADER: { const ShaderInfo *shader_info_in = (const ShaderInfo *)p_driver_id.id; - context->set_object_name(shader_info_in->root_signature.Get(), p_name); + _set_object_name(shader_info_in->root_signature.Get(), p_name); } break; case OBJECT_TYPE_UNIFORM_SET: { const UniformSetInfo *uniform_set_info = (const UniformSetInfo *)p_driver_id.id; if (uniform_set_info->desc_heaps.resources.get_heap()) { - context->set_object_name(uniform_set_info->desc_heaps.resources.get_heap(), p_name + " resources heap"); + _set_object_name(uniform_set_info->desc_heaps.resources.get_heap(), p_name + " resources heap"); } if (uniform_set_info->desc_heaps.samplers.get_heap()) { - context->set_object_name(uniform_set_info->desc_heaps.samplers.get_heap(), p_name + " samplers heap"); + _set_object_name(uniform_set_info->desc_heaps.samplers.get_heap(), p_name + " samplers heap"); } } break; case OBJECT_TYPE_PIPELINE: { ID3D12PipelineState *pso = (ID3D12PipelineState *)p_driver_id.id; - context->set_object_name(pso, p_name); + _set_object_name(pso, p_name); } break; default: { DEV_ASSERT(false); @@ -5287,10 +5715,10 @@ void RenderingDeviceDriverD3D12::set_object_name(ObjectType p_type, ID p_driver_ uint64_t RenderingDeviceDriverD3D12::get_resource_native_handle(DriverResource p_type, ID p_driver_id) { switch (p_type) { case DRIVER_RESOURCE_LOGICAL_DEVICE: { - return (uint64_t)device; + return (uint64_t)device.Get(); } case DRIVER_RESOURCE_PHYSICAL_DEVICE: { - return (uint64_t)context->get_adapter(); + return (uint64_t)adapter.Get(); } case DRIVER_RESOURCE_TOPMOST_OBJECT: { return 0; @@ -5342,7 +5770,7 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) { case LIMIT_MAX_BOUND_UNIFORM_SETS: return safe_unbounded; case LIMIT_MAX_TEXTURES_PER_SHADER_STAGE: - return context->get_device_limits().max_srvs_per_shader_stage; + return device_limits.max_srvs_per_shader_stage; case LIMIT_MAX_UNIFORM_BUFFER_SIZE: return 65536; case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: @@ -5364,22 +5792,15 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) { // Note in min/max. Shader model 6.6 supports it (see https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_WaveSize.html), // but at this time I don't know the implications on the transpilation to DXIL, etc. case LIMIT_SUBGROUP_MIN_SIZE: - case LIMIT_SUBGROUP_MAX_SIZE: { - const D3D12Context::SubgroupCapabilities &subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_MAX_SIZE: return subgroup_capabilities.size; - } - case LIMIT_SUBGROUP_IN_SHADERS: { - const D3D12Context::SubgroupCapabilities &subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_IN_SHADERS: return subgroup_capabilities.supported_stages_flags_rd(); - } - case LIMIT_SUBGROUP_OPERATIONS: { - const D3D12Context::SubgroupCapabilities &subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_OPERATIONS: return subgroup_capabilities.supported_operations_flags_rd(); - } case LIMIT_VRS_TEXEL_WIDTH: - case LIMIT_VRS_TEXEL_HEIGHT: { - return context->get_vrs_capabilities().ss_image_tile_size; - } + case LIMIT_VRS_TEXEL_HEIGHT: + return vrs_capabilities.ss_image_tile_size; default: { #ifdef DEV_ENABLED WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + "."); @@ -5412,100 +5833,45 @@ uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) { bool RenderingDeviceDriverD3D12::has_feature(Features p_feature) { switch (p_feature) { - case SUPPORTS_MULTIVIEW: { - const RDD::MultiviewCapabilities &multiview_capabilies = context->get_multiview_capabilities(); - return multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; - } break; - case SUPPORTS_FSR_HALF_FLOAT: { - return context->get_shader_capabilities().native_16bit_ops && context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; - } break; - case SUPPORTS_ATTACHMENT_VRS: { - const D3D12Context::VRSCapabilities &vrs_capabilities = context->get_vrs_capabilities(); + case SUPPORTS_MULTIVIEW: + return multiview_capabilities.is_supported && multiview_capabilities.max_view_count > 1; + case SUPPORTS_FSR_HALF_FLOAT: + return shader_capabilities.native_16bit_ops && storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + case SUPPORTS_ATTACHMENT_VRS: return vrs_capabilities.ss_image_supported; - } break; - case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: { + case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; - } break; - default: { + default: return false; - } } } const RDD::MultiviewCapabilities &RenderingDeviceDriverD3D12::get_multiview_capabilities() { - return context->get_multiview_capabilities(); + return multiview_capabilities; +} + +String RenderingDeviceDriverD3D12::get_api_name() const { + return "D3D12"; +} + +String RenderingDeviceDriverD3D12::get_api_version() const { + return vformat("%d_%d", feature_level / 10, feature_level % 10); +} + +String RenderingDeviceDriverD3D12::get_pipeline_cache_uuid() const { + return pipeline_cache_id; +} + +const RDD::Capabilities &RenderingDeviceDriverD3D12::get_capabilities() const { + return device_capabilities; } /******************/ -RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(D3D12Context *p_context, ID3D12Device *p_device, uint32_t p_frame_count) : - context(p_context), - device(p_device) { - D3D12MA::ALLOCATOR_DESC allocator_desc = {}; - allocator_desc.pDevice = device; - allocator_desc.pAdapter = context->get_adapter(); +RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver) { + DEV_ASSERT(p_context_driver != nullptr); - HRESULT res = D3D12MA::CreateAllocator(&allocator_desc, &allocator); - ERR_FAIL_COND_MSG(!SUCCEEDED(res), "D3D12MA::CreateAllocator failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); - - { - uint32_t resource_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame"); - uint32_t sampler_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"); - uint32_t misc_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"); - - frames.resize(p_frame_count); - for (uint32_t i = 0; i < frames.size(); i++) { - Error err = frames[i].desc_heaps.resources.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, resource_descriptors_per_frame, true); - ERR_FAIL_COND_MSG(err, "Creating the frame's RESOURCE descriptors heap failed."); - err = frames[i].desc_heaps.samplers.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, sampler_descriptors_per_frame, true); - ERR_FAIL_COND_MSG(err, "Creating the frame's SAMPLER descriptors heap failed."); - err = frames[i].desc_heaps.aux.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, misc_descriptors_per_frame, false); - ERR_FAIL_COND_MSG(err, "Creating the frame's AUX descriptors heap failed."); - err = frames[i].desc_heaps.rtv.allocate(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, misc_descriptors_per_frame, false); - ERR_FAIL_COND_MSG(err, "Creating the frame's RENDER TARGET descriptors heap failed."); - - frames[i].desc_heap_walkers.resources = frames[i].desc_heaps.resources.make_walker(); - frames[i].desc_heap_walkers.samplers = frames[i].desc_heaps.samplers.make_walker(); - frames[i].desc_heap_walkers.aux = frames[i].desc_heaps.aux.make_walker(); - frames[i].desc_heap_walkers.rtv = frames[i].desc_heaps.rtv.make_walker(); - - { - D3D12MA::ALLOCATION_DESC allocation_desc = {}; - allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT; - - CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); - - ID3D12Resource *resource = nullptr; - res = allocator->CreateResource( - &allocation_desc, - &resource_desc, - D3D12_RESOURCE_STATE_COMMON, - nullptr, - &frames[frame_idx].aux_resource, - IID_PPV_ARGS(&resource)); - ERR_FAIL_COND_MSG(!SUCCEEDED(res), "D3D12MA::CreateResource failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); - } - } - } - - { // Create command signatures for indirect commands. - auto _create_command_signature = [&](D3D12_INDIRECT_ARGUMENT_TYPE p_type, uint32_t p_stride, ComPtr *r_cmd_sig) { - D3D12_INDIRECT_ARGUMENT_DESC iarg_desc = {}; - iarg_desc.Type = p_type; - D3D12_COMMAND_SIGNATURE_DESC cs_desc = {}; - cs_desc.ByteStride = p_stride; - cs_desc.NumArgumentDescs = 1; - cs_desc.pArgumentDescs = &iarg_desc; - cs_desc.NodeMask = 0; - res = device->CreateCommandSignature(&cs_desc, nullptr, IID_PPV_ARGS(r_cmd_sig->GetAddressOf())); - ERR_FAIL_COND_MSG(!SUCCEEDED(res), "CreateCommandSignature failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); - }; - _create_command_signature(D3D12_INDIRECT_ARGUMENT_TYPE_DRAW, sizeof(D3D12_DRAW_ARGUMENTS), &indirect_cmd_signatures.draw); - _create_command_signature(D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED, sizeof(D3D12_DRAW_INDEXED_ARGUMENTS), &indirect_cmd_signatures.draw_indexed); - _create_command_signature(D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH, sizeof(D3D12_DISPATCH_ARGUMENTS), &indirect_cmd_signatures.dispatch); - } - - glsl_type_singleton_init_or_ref(); + this->context_driver = p_context_driver; } RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() { @@ -5518,3 +5884,357 @@ RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() { glsl_type_singleton_decref(); } + +Error RenderingDeviceDriverD3D12::_initialize_device() { + HRESULT res; + 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())); + } else { + res = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, 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 (context_driver->use_validation_layers()) { + ComPtr info_queue; + res = device.As(&info_queue); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + +#if CUSTOM_INFO_QUEUE_ENABLED + ComPtr info_queue_1; + device.As(&info_queue_1); + if (info_queue_1) { + // Custom printing supported (added in Windows 10 Release Preview build 20236). Even if the callback cookie is unused, it seems the + // argument is not optional and the function will fail if it's not specified. + DWORD callback_cookie; + info_queue_1->SetMuteDebugOutput(TRUE); + res = info_queue_1->RegisterMessageCallback(&_debug_message_func, D3D12_MESSAGE_CALLBACK_IGNORE_FILTERS, nullptr, &callback_cookie); + ERR_FAIL_COND_V(!SUCCEEDED(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(!SUCCEEDED(res), ERR_CANT_CREATE); + res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); + ERR_FAIL_COND_V(!SUCCEEDED(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; seems benign. + 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(!SUCCEEDED(res), ERR_CANT_CREATE); + } + + return OK; +} + +Error RenderingDeviceDriverD3D12::_check_capabilities() { + // Check feature levels. + 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 = device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &feat_levels, sizeof(feat_levels)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_UNAVAILABLE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)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; + + // Fill device capabilities. + device_capabilities.device_family = DEVICE_DIRECTX; + device_capabilities.version_major = feature_level / 10; + device_capabilities.version_minor = feature_level % 10; + + // 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; + + // Check shader model. + D3D12_FEATURE_DATA_SHADER_MODEL shader_model = {}; + shader_model.HighestShaderModel = MIN(D3D_HIGHEST_SHADER_MODEL, D3D_SHADER_MODEL_6_6); + res = device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)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 = {}; + res = 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 = 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 = 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_OPTIONS4 options4 = {}; + res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4)); + if (SUCCEEDED(res)) { + shader_capabilities.native_16bit_ops = options4.Native16BitShaderOpsSupported; + } + + D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = {}; + res = 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 = 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"); + } + + print_verbose(String("- D3D12 16-bit ops supported: ") + (shader_capabilities.native_16bit_ops ? "yes" : "no")); + + return OK; +} + +Error RenderingDeviceDriverD3D12::_get_device_limits() { + D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; + HRESULT res = device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_UNAVAILABLE, "CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + // https://docs.microsoft.com/en-us/windows/win32/direct3d12/hardware-support + device_limits.max_srvs_per_shader_stage = options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1 ? 128 : UINT64_MAX; + device_limits.max_cbvs_per_shader_stage = options.ResourceBindingTier <= D3D12_RESOURCE_BINDING_TIER_2 ? 14 : UINT64_MAX; + device_limits.max_samplers_across_all_stages = options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1 ? 16 : 2048; + if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1) { + device_limits.max_uavs_across_all_stages = feature_level <= 110 ? 8 : 64; + } else if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_2) { + device_limits.max_uavs_across_all_stages = 64; + } else { + device_limits.max_uavs_across_all_stages = UINT64_MAX; + } + + // Retrieving the timestamp frequency requires creating a command queue that will be discarded immediately. + ComPtr unused_command_queue; + D3D12_COMMAND_QUEUE_DESC queue_desc = {}; + queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + res = device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(unused_command_queue.GetAddressOf())); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = unused_command_queue->GetTimestampFrequency(&device_limits.timestamp_frequency); + if (!SUCCEEDED(res)) { + print_verbose("D3D12: GetTimestampFrequency failed with error " + vformat("0x%08ux", (uint64_t)res) + ". Timestamps will be inaccurate."); + } + + return OK; +} + +Error RenderingDeviceDriverD3D12::_initialize_allocator() { + D3D12MA::ALLOCATOR_DESC allocator_desc = {}; + allocator_desc.pDevice = device.Get(); + allocator_desc.pAdapter = adapter.Get(); + + HRESULT res = D3D12MA::CreateAllocator(&allocator_desc, &allocator); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "D3D12MA::CreateAllocator failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + + return OK; +} + +static Error create_command_signature(ID3D12Device *device, D3D12_INDIRECT_ARGUMENT_TYPE p_type, uint32_t p_stride, ComPtr *r_cmd_sig) { + D3D12_INDIRECT_ARGUMENT_DESC iarg_desc = {}; + iarg_desc.Type = p_type; + D3D12_COMMAND_SIGNATURE_DESC cs_desc = {}; + cs_desc.ByteStride = p_stride; + cs_desc.NumArgumentDescs = 1; + cs_desc.pArgumentDescs = &iarg_desc; + cs_desc.NodeMask = 0; + HRESULT res = device->CreateCommandSignature(&cs_desc, nullptr, IID_PPV_ARGS(r_cmd_sig->GetAddressOf())); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "CreateCommandSignature failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + return OK; +}; + +Error RenderingDeviceDriverD3D12::_initialize_frames(uint32_t p_frame_count) { + Error err; + D3D12MA::ALLOCATION_DESC allocation_desc = {}; + allocation_desc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + uint32_t resource_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame"); + uint32_t sampler_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"); + uint32_t misc_descriptors_per_frame = GLOBAL_GET("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"); + + frames.resize(p_frame_count); + for (uint32_t i = 0; i < frames.size(); i++) { + err = frames[i].desc_heaps.resources.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, resource_descriptors_per_frame, true); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Creating the frame's RESOURCE descriptors heap failed."); + + err = frames[i].desc_heaps.samplers.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, sampler_descriptors_per_frame, true); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Creating the frame's SAMPLER descriptors heap failed."); + + err = frames[i].desc_heaps.aux.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, misc_descriptors_per_frame, false); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Creating the frame's AUX descriptors heap failed."); + + err = frames[i].desc_heaps.rtv.allocate(device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, misc_descriptors_per_frame, false); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Creating the frame's RENDER TARGET descriptors heap failed."); + + frames[i].desc_heap_walkers.resources = frames[i].desc_heaps.resources.make_walker(); + frames[i].desc_heap_walkers.samplers = frames[i].desc_heaps.samplers.make_walker(); + frames[i].desc_heap_walkers.aux = frames[i].desc_heaps.aux.make_walker(); + frames[i].desc_heap_walkers.rtv = frames[i].desc_heaps.rtv.make_walker(); + + ID3D12Resource *resource = nullptr; + HRESULT res = allocator->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COMMON, nullptr, &frames[frame_idx].aux_resource, IID_PPV_ARGS(&resource)); + ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "D3D12MA::CreateResource failed with error " + vformat("0x%08ux", (uint64_t)res) + "."); + } + + return OK; +} + +Error RenderingDeviceDriverD3D12::_initialize_command_signatures() { + Error err = create_command_signature(device.Get(), D3D12_INDIRECT_ARGUMENT_TYPE_DRAW, sizeof(D3D12_DRAW_ARGUMENTS), &indirect_cmd_signatures.draw); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = create_command_signature(device.Get(), D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED, sizeof(D3D12_DRAW_INDEXED_ARGUMENTS), &indirect_cmd_signatures.draw_indexed); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = create_command_signature(device.Get(), D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH, sizeof(D3D12_DISPATCH_ARGUMENTS), &indirect_cmd_signatures.dispatch); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + return OK; +} + +Error RenderingDeviceDriverD3D12::initialize(uint32_t p_device_index, uint32_t p_frame_count) { + context_device = context_driver->device_get(p_device_index); + adapter = context_driver->create_adapter(p_device_index); + ERR_FAIL_NULL_V(adapter, ERR_CANT_CREATE); + + HRESULT res = adapter->GetDesc(&adapter_desc); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + // Set the pipeline cache ID based on the adapter information. + pipeline_cache_id = String::hex_encode_buffer((uint8_t *)&adapter_desc.AdapterLuid, sizeof(LUID)); + pipeline_cache_id += "-driver-" + itos(adapter_desc.Revision); + + Error err = _initialize_device(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = _check_capabilities(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = _get_device_limits(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = _initialize_allocator(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = _initialize_frames(p_frame_count); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + err = _initialize_command_signatures(); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + glsl_type_singleton_init_or_ref(); + + return OK; +} diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index 0da339c6fdb..5fe5aff6cf1 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -65,14 +65,11 @@ using Microsoft::WRL::ComPtr; #define D3D12_BITCODE_OFFSETS_NUM_STAGES 3 struct dxil_validator; - -class D3D12Context; +class RenderingContextDriverD3D12; // Design principles: // - D3D12 structs are zero-initialized and fields not requiring a non-zero value are omitted (except in cases where expresivity reasons apply). class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { - friend class D3D12Context; // For FramebufferInfo, RenderPassInfo and CommandBufferInfo. - /*****************/ /**** GENERIC ****/ /*****************/ @@ -86,8 +83,58 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { static const D3D12Format RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX]; - D3D12Context *context = nullptr; - ID3D12Device *device = nullptr; // Owned by the context. + struct DeviceLimits { + uint64_t max_srvs_per_shader_stage = 0; + uint64_t max_cbvs_per_shader_stage = 0; + uint64_t max_samplers_across_all_stages = 0; + uint64_t max_uavs_across_all_stages = 0; + uint64_t timestamp_frequency = 0; + }; + + struct SubgroupCapabilities { + uint32_t size = 0; + bool wave_ops_supported = false; + uint32_t supported_stages_flags_rd() const; + uint32_t supported_operations_flags_rd() const; + }; + + struct VRSCapabilities { + bool draw_call_supported = false; // We can specify our fragment rate on a draw call level. + bool primitive_supported = false; // We can specify our fragment rate on each drawcall. + bool primitive_in_multiviewport = false; + bool ss_image_supported = false; // We can provide a density map attachment on our framebuffer. + uint32_t ss_image_tile_size = 0; + bool additional_rates_supported = false; + }; + + struct ShaderCapabilities { + D3D_SHADER_MODEL shader_model = (D3D_SHADER_MODEL)0; + bool native_16bit_ops = false; + }; + + struct StorageBufferCapabilities { + bool storage_buffer_16_bit_access_is_supported = false; + }; + + struct FormatCapabilities { + bool relaxed_casting_supported = false; + }; + + RenderingContextDriverD3D12 *context_driver = nullptr; + RenderingContextDriver::Device context_device; + ComPtr adapter; + DXGI_ADAPTER_DESC adapter_desc; + ComPtr device; + DeviceLimits device_limits; + RDD::Capabilities device_capabilities; + uint32_t feature_level = 0; // Major * 10 + minor. + SubgroupCapabilities subgroup_capabilities; + RDD::MultiviewCapabilities multiview_capabilities; + VRSCapabilities vrs_capabilities; + ShaderCapabilities shader_capabilities; + StorageBufferCapabilities storage_buffer_capabilities; + FormatCapabilities format_capabilities; + String pipeline_cache_id; class DescriptorsHeap { D3D12_DESCRIPTOR_HEAP_DESC desc = {}; @@ -127,6 +174,19 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { ComPtr dispatch; } indirect_cmd_signatures; + static void STDMETHODCALLTYPE _debug_message_func(D3D12_MESSAGE_CATEGORY p_category, D3D12_MESSAGE_SEVERITY p_severity, D3D12_MESSAGE_ID p_id, LPCSTR p_description, void *p_context); + void _set_object_name(ID3D12Object *p_object, String p_object_name); + Error _initialize_device(); + Error _check_capabilities(); + Error _get_device_limits(); + Error _initialize_allocator(); + Error _initialize_frames(uint32_t p_frame_count); + Error _initialize_command_signatures(); + +public: + Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final; + +private: /****************/ /**** MEMORY ****/ /****************/ @@ -183,7 +243,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver { uint64_t subres_mask[MAX_SUBRESOURCES / 64] = {}; } groups[MAX_GROUPS]; uint8_t groups_count = 0; - static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATE_COMMON; + static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATES(0xFFFFFFFFU); }; PagedAllocator> res_barriers_requests_allocator; HashMap, decltype(res_barriers_requests_allocator)> res_barriers_requests; @@ -307,13 +367,65 @@ public: VectorView p_buffer_barriers, VectorView p_texture_barriers) override final; - /*************************/ - /**** COMMAND BUFFERS ****/ - /*************************/ +private: + /****************/ + /**** FENCES ****/ + /****************/ + struct FenceInfo { + ComPtr d3d_fence = nullptr; + HANDLE event_handle = NULL; + UINT64 fence_value = 0; + }; + +public: + virtual FenceID fence_create() override; + virtual Error fence_wait(FenceID p_fence) override; + virtual void fence_free(FenceID p_fence) override; + +private: + /********************/ + /**** SEMAPHORES ****/ + /********************/ + + struct SemaphoreInfo { + ComPtr d3d_fence = nullptr; + UINT64 fence_value = 0; + }; + + virtual SemaphoreID semaphore_create() override; + virtual void semaphore_free(SemaphoreID p_semaphore) override; + + /******************/ + /**** COMMANDS ****/ + /******************/ + + // ----- QUEUE FAMILY ----- + + virtual CommandQueueFamilyID command_queue_family_get(BitField p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override; + +private: + // ----- QUEUE ----- + + struct CommandQueueInfo { + ComPtr d3d_queue; + }; + +public: + virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override; + virtual Error command_queue_execute(CommandQueueID p_cmd_queue, VectorView p_cmd_buffers, VectorView p_wait_semaphores, VectorView p_signal_semaphores, FenceID p_signal_fence) override; + virtual Error command_queue_present(CommandQueueID p_cmd_queue, VectorView p_swap_chains, VectorView p_wait_semaphores) override; + virtual void command_queue_free(CommandQueueID p_cmd_queue) override; + +private: // ----- POOL ----- + struct CommandPoolInfo { + CommandQueueFamilyID queue_family; + CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY; + }; - virtual CommandPoolID command_pool_create(CommandBufferType p_cmd_buffer_type) override final; +public: + virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final; virtual void command_pool_free(CommandPoolID p_cmd_pool) override final; // ----- BUFFER ----- @@ -347,17 +459,45 @@ private: uint32_t compute_root_signature_crc = 0; RenderPassState render_pass_state; + bool descriptor_heaps_set = false; }; - RBMap> pools_command_buffers; - CommandPoolID last_command_pool_id; public: - virtual CommandBufferID command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) override final; + virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final; virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final; virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final; virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final; virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView p_secondary_cmd_buffers) override final; +private: + /********************/ + /**** SWAP CHAIN ****/ + /********************/ + + struct SwapChain { + ComPtr d3d_swap_chain; + RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID(); + UINT present_flags = 0; + UINT sync_interval = 1; + UINT creation_flags = 0; + RenderPassID render_pass; + TightLocalVector render_targets; + TightLocalVector render_targets_info; + TightLocalVector framebuffers; + RDD::DataFormat data_format = DATA_FORMAT_MAX; + }; + + void _swap_chain_release(SwapChain *p_swap_chain); + void _swap_chain_release_buffers(SwapChain *p_swap_chain); + +public: + virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override; + virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override; + virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override; + virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override; + virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override; + virtual void swap_chain_free(SwapChainID p_swap_chain) override; + /*********************/ /**** FRAMEBUFFER ****/ /*********************/ @@ -376,6 +516,8 @@ private: D3D12_RENDER_TARGET_VIEW_DESC _make_rtv_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true); D3D12_DEPTH_STENCIL_VIEW_DESC _make_dsv_for_texture(const TextureInfo *p_texture_info); + FramebufferID _framebuffer_create(RenderPassID p_render_pass, VectorView p_attachments, uint32_t p_width, uint32_t p_height, bool p_is_screen); + public: virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView p_attachments, uint32_t p_width, uint32_t p_height) override final; virtual void framebuffer_free(FramebufferID p_framebuffer) override final; @@ -602,6 +744,7 @@ public: virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final; private: + void _command_check_descriptor_sets(CommandBufferID p_cmd_buffer); void _command_bind_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index, bool p_for_compute); public: @@ -777,12 +920,6 @@ public: virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final; virtual void command_end_label(CommandBufferID p_cmd_buffer) override final; - /****************/ - /**** SCREEN ****/ - /****************/ - - virtual DataFormat screen_get_format() override final; - /********************/ /**** SUBMISSION ****/ /********************/ @@ -821,7 +958,7 @@ private: bool segment_begun = false; public: - virtual void begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) override final; + virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final; virtual void end_segment() override final; /**************/ @@ -835,6 +972,10 @@ public: virtual uint64_t api_trait_get(ApiTrait p_trait) override final; virtual bool has_feature(Features p_feature) override final; virtual const MultiviewCapabilities &get_multiview_capabilities() override final; + virtual String get_api_name() const override final; + virtual String get_api_version() const override final; + virtual String get_pipeline_cache_uuid() const override final; + virtual const Capabilities &get_capabilities() const override final; private: /*********************/ @@ -858,7 +999,7 @@ private: /******************/ public: - RenderingDeviceDriverD3D12(D3D12Context *p_context, ID3D12Device *p_device, uint32_t p_frame_count); + RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver); virtual ~RenderingDeviceDriverD3D12(); }; diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 6ab98a9473f..0a20d838362 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -356,9 +356,6 @@ RasterizerGLES3::RasterizerGLES3() { RasterizerGLES3::~RasterizerGLES3() { } -void RasterizerGLES3::prepare_for_blitting_render_targets() { -} - void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, bool p_first) { GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index d9f436a2ec4..cf3cedfea16 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -90,7 +90,6 @@ public: void initialize(); void begin_frame(double frame_step); - void prepare_for_blitting_render_targets(); void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); void end_viewport(bool p_swap_buffers); diff --git a/drivers/vulkan/rendering_context_driver_vulkan.cpp b/drivers/vulkan/rendering_context_driver_vulkan.cpp new file mode 100644 index 00000000000..6eb25743f92 --- /dev/null +++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp @@ -0,0 +1,686 @@ +/**************************************************************************/ +/* rendering_context_driver_vulkan.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. */ +/**************************************************************************/ + +#ifdef VULKAN_ENABLED + +#include "rendering_context_driver_vulkan.h" + +#include "vk_enum_string_helper.h" + +#include "core/config/project_settings.h" +#include "core/version.h" + +#include "rendering_device_driver_vulkan.h" +#include "vulkan_hooks.h" + +RenderingContextDriverVulkan::RenderingContextDriverVulkan() { + // Empty constructor. +} + +RenderingContextDriverVulkan::~RenderingContextDriverVulkan() { + if (debug_messenger != VK_NULL_HANDLE && functions.DestroyDebugUtilsMessengerEXT != nullptr) { + functions.DestroyDebugUtilsMessengerEXT(instance, debug_messenger, nullptr); + } + + if (debug_report != VK_NULL_HANDLE && functions.DestroyDebugReportCallbackEXT != nullptr) { + functions.DestroyDebugReportCallbackEXT(instance, debug_report, nullptr); + } + + if (instance != VK_NULL_HANDLE) { + vkDestroyInstance(instance, nullptr); + } +} + +Error RenderingContextDriverVulkan::_initialize_vulkan_version() { + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description + // For Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. + typedef VkResult(VKAPI_PTR * _vkEnumerateInstanceVersion)(uint32_t *); + _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); + if (func != nullptr) { + uint32_t api_version; + VkResult res = func(&api_version); + if (res == VK_SUCCESS) { + instance_api_version = api_version; + } else { + // According to the documentation this shouldn't fail with anything except a memory allocation error + // in which case we're in deep trouble anyway. + ERR_FAIL_V(ERR_CANT_CREATE); + } + } else { + print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0."); + instance_api_version = VK_API_VERSION_1_0; + } + + return OK; +} + +void RenderingContextDriverVulkan::_register_requested_instance_extension(const CharString &p_extension_name, bool p_required) { + ERR_FAIL_COND(requested_instance_extensions.has(p_extension_name)); + requested_instance_extensions[p_extension_name] = p_required; +} + +Error RenderingContextDriverVulkan::_initialize_instance_extensions() { + enabled_instance_extension_names.clear(); + + // The surface extension and the platform-specific surface extension are core requirements. + _register_requested_instance_extension(VK_KHR_SURFACE_EXTENSION_NAME, true); + if (_get_platform_surface_extension()) { + _register_requested_instance_extension(_get_platform_surface_extension(), true); + } + + if (_use_validation_layers()) { + _register_requested_instance_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false); + } + + // This extension allows us to use the properties2 features to query additional device capabilities. + _register_requested_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); + + // Only enable debug utils in verbose mode or DEV_ENABLED. + // End users would get spammed with messages of varying verbosity due to the + // mess that thirdparty layers/extensions and drivers seem to leave in their + // wake, making the Windows registry a bottomless pit of broken layer JSON. +#ifdef DEV_ENABLED + bool want_debug_utils = true; +#else + bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); +#endif + if (want_debug_utils) { + _register_requested_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false); + } + + // Load instance extensions that are available. + uint32_t instance_extension_count = 0; + VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(instance_extension_count == 0, ERR_CANT_CREATE, "No instance extensions were found."); + + TightLocalVector instance_extensions; + instance_extensions.resize(instance_extension_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions.ptr()); + if (err != VK_SUCCESS && err != VK_INCOMPLETE) { + ERR_FAIL_V(ERR_CANT_CREATE); + } + +#ifdef DEV_ENABLED + for (uint32_t i = 0; i < instance_extension_count; i++) { + print_verbose(String("VULKAN: Found instance extension ") + String::utf8(instance_extensions[i].extensionName) + String(".")); + } +#endif + + // Enable all extensions that are supported and requested. + for (uint32_t i = 0; i < instance_extension_count; i++) { + CharString extension_name(instance_extensions[i].extensionName); + if (requested_instance_extensions.has(extension_name)) { + enabled_instance_extension_names.insert(extension_name); + } + } + + // Now check our requested extensions. + for (KeyValue &requested_extension : requested_instance_extensions) { + if (!enabled_instance_extension_names.has(requested_extension.key)) { + if (requested_extension.value) { + ERR_FAIL_V_MSG(ERR_BUG, String("Required extension ") + String::utf8(requested_extension.key) + String(" not found.")); + } else { + print_verbose(String("Optional extension ") + String::utf8(requested_extension.key) + String(" not found.")); + } + } + } + + return OK; +} + +Error RenderingContextDriverVulkan::_find_validation_layers(TightLocalVector &r_layer_names) const { + r_layer_names.clear(); + + uint32_t instance_layer_count = 0; + VkResult err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + if (instance_layer_count > 0) { + TightLocalVector layer_properties; + layer_properties.resize(instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, layer_properties.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + // Preferred set of validation layers. + const std::initializer_list preferred = { "VK_LAYER_KHRONOS_validation" }; + + // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers. + const std::initializer_list lunarg = { "VK_LAYER_LUNARG_standard_validation" }; + + // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers. + const std::initializer_list google = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" }; + + // Verify all the layers of the list are present. + for (const std::initializer_list &list : { preferred, lunarg, google }) { + bool layers_found = false; + for (const char *layer_name : list) { + layers_found = false; + + for (const VkLayerProperties &properties : layer_properties) { + if (!strcmp(properties.layerName, layer_name)) { + layers_found = true; + break; + } + } + + if (!layers_found) { + break; + } + } + + if (layers_found) { + r_layer_names.reserve(list.size()); + for (const char *layer_name : list) { + r_layer_names.push_back(layer_name); + } + + break; + } + } + } + + return OK; +} + +VKAPI_ATTR VkBool32 VKAPI_CALL RenderingContextDriverVulkan::_debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, VkDebugUtilsMessageTypeFlagsEXT p_message_type, const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) { + // This error needs to be ignored because the AMD allocator will mix up memory types on IGP processors. + if (strstr(p_callback_data->pMessage, "Mapping an image with layout") != nullptr && strstr(p_callback_data->pMessage, "can result in undefined behavior if this memory is used by the device") != nullptr) { + return VK_FALSE; + } + // This needs to be ignored because Validator is wrong here. + if (strstr(p_callback_data->pMessage, "Invalid SPIR-V binary version 1.3") != nullptr) { + return VK_FALSE; + } + // This needs to be ignored because Validator is wrong here. + if (strstr(p_callback_data->pMessage, "Shader requires flag") != nullptr) { + return VK_FALSE; + } + + // This needs to be ignored because Validator is wrong here. + if (strstr(p_callback_data->pMessage, "SPIR-V module not valid: Pointer operand") != nullptr && strstr(p_callback_data->pMessage, "must be a memory object") != nullptr) { + return VK_FALSE; + } + + if (p_callback_data->pMessageIdName && strstr(p_callback_data->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { + return VK_FALSE; + } + + String type_string; + switch (p_message_type) { + case (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT): + type_string = "GENERAL"; + break; + case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT): + type_string = "VALIDATION"; + break; + case (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): + type_string = "PERFORMANCE"; + break; + case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): + type_string = "VALIDATION|PERFORMANCE"; + break; + } + + String objects_string; + if (p_callback_data->objectCount > 0) { + objects_string = "\n\tObjects - " + String::num_int64(p_callback_data->objectCount); + for (uint32_t object = 0; object < p_callback_data->objectCount; ++object) { + objects_string += + "\n\t\tObject[" + String::num_int64(object) + "]" + + " - " + string_VkObjectType(p_callback_data->pObjects[object].objectType) + + ", Handle " + String::num_int64(p_callback_data->pObjects[object].objectHandle); + + if (p_callback_data->pObjects[object].pObjectName != nullptr && strlen(p_callback_data->pObjects[object].pObjectName) > 0) { + objects_string += ", Name \"" + String(p_callback_data->pObjects[object].pObjectName) + "\""; + } + } + } + + String labels_string; + if (p_callback_data->cmdBufLabelCount > 0) { + labels_string = "\n\tCommand Buffer Labels - " + String::num_int64(p_callback_data->cmdBufLabelCount); + for (uint32_t cmd_buf_label = 0; cmd_buf_label < p_callback_data->cmdBufLabelCount; ++cmd_buf_label) { + labels_string += + "\n\t\tLabel[" + String::num_int64(cmd_buf_label) + "]" + + " - " + p_callback_data->pCmdBufLabels[cmd_buf_label].pLabelName + + "{ "; + + for (int color_idx = 0; color_idx < 4; ++color_idx) { + labels_string += String::num(p_callback_data->pCmdBufLabels[cmd_buf_label].color[color_idx]); + if (color_idx < 3) { + labels_string += ", "; + } + } + + labels_string += " }"; + } + } + + String error_message(type_string + + " - Message Id Number: " + String::num_int64(p_callback_data->messageIdNumber) + + " | Message Id Name: " + p_callback_data->pMessageIdName + + "\n\t" + p_callback_data->pMessage + + objects_string + labels_string); + + // Convert VK severity to our own log macros. + switch (p_message_severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + print_verbose(error_message); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + print_line(error_message); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN_PRINT(error_message); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + 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; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT: + break; // Shouldn't happen, only handling to make compilers happy. + } + + return VK_FALSE; +} + +VKAPI_ATTR VkBool32 VKAPI_CALL RenderingContextDriverVulkan::_debug_report_callback(VkDebugReportFlagsEXT p_flags, VkDebugReportObjectTypeEXT p_object_type, uint64_t p_object, size_t p_location, int32_t p_message_code, const char *p_layer_prefix, const char *p_message, void *p_user_data) { + String debug_message = String("Vulkan Debug Report: object - ") + String::num_int64(p_object) + "\n" + p_message; + + switch (p_flags) { + case VK_DEBUG_REPORT_DEBUG_BIT_EXT: + case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: + print_line(debug_message); + break; + case VK_DEBUG_REPORT_WARNING_BIT_EXT: + case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: + WARN_PRINT(debug_message); + break; + case VK_DEBUG_REPORT_ERROR_BIT_EXT: + ERR_PRINT(debug_message); + break; + } + + return VK_FALSE; +} + +Error RenderingContextDriverVulkan::_initialize_instance() { + Error err; + TightLocalVector enabled_extension_names; + enabled_extension_names.reserve(enabled_instance_extension_names.size()); + for (const CharString &extension_name : enabled_instance_extension_names) { + enabled_extension_names.push_back(extension_name.ptr()); + } + + // We'll set application version to the Vulkan version we're developing against, even if our instance is based on an older Vulkan + // version, devices can still support newer versions of Vulkan. The exception is when we're on Vulkan 1.0, we should not set this + // to anything but 1.0. Note that this value is only used by validation layers to warn us about version issues. + uint32_t application_api_version = instance_api_version == VK_API_VERSION_1_0 ? VK_API_VERSION_1_0 : VK_API_VERSION_1_2; + + CharString cs = GLOBAL_GET("application/config/name").operator String().utf8(); + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = cs.get_data(); + app_info.pEngineName = VERSION_NAME; + app_info.engineVersion = VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + app_info.apiVersion = application_api_version; + + TightLocalVector enabled_layer_names; + if (_use_validation_layers()) { + err = _find_validation_layers(enabled_layer_names); + ERR_FAIL_COND_V(err != OK, err); + } + + VkInstanceCreateInfo instance_info = {}; + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app_info; + instance_info.enabledExtensionCount = enabled_extension_names.size(); + instance_info.ppEnabledExtensionNames = enabled_extension_names.ptr(); + instance_info.enabledLayerCount = enabled_layer_names.size(); + instance_info.ppEnabledLayerNames = enabled_layer_names.ptr(); + + // This is info for a temp callback to use during CreateInstance. After the instance is created, we use the instance-based function to register the final callback. + VkDebugUtilsMessengerCreateInfoEXT debug_messenger_create_info = {}; + VkDebugReportCallbackCreateInfoEXT debug_report_callback_create_info = {}; + const bool has_debug_utils_extension = enabled_instance_extension_names.has(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + const bool has_debug_report_extension = enabled_instance_extension_names.has(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + if (has_debug_utils_extension) { + debug_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + debug_messenger_create_info.pNext = nullptr; + debug_messenger_create_info.flags = 0; + debug_messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + debug_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debug_messenger_create_info.pfnUserCallback = _debug_messenger_callback; + debug_messenger_create_info.pUserData = this; + instance_info.pNext = &debug_messenger_create_info; + } else if (has_debug_report_extension) { + debug_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT; + debug_report_callback_create_info.pfnCallback = _debug_report_callback; + debug_report_callback_create_info.pUserData = this; + instance_info.pNext = &debug_report_callback_create_info; + } + + err = _create_vulkan_instance(&instance_info, &instance); + ERR_FAIL_COND_V(err != OK, err); + +#ifdef USE_VOLK + volkLoadInstance(instance); +#endif + + // Physical device. + if (enabled_instance_extension_names.has(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { + functions.GetPhysicalDeviceFeatures2 = PFN_vkGetPhysicalDeviceFeatures2(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2")); + functions.GetPhysicalDeviceProperties2 = PFN_vkGetPhysicalDeviceProperties2(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2")); + + // In Vulkan 1.0, the functions might be accessible under their original extension names. + if (functions.GetPhysicalDeviceFeatures2 == nullptr) { + functions.GetPhysicalDeviceFeatures2 = PFN_vkGetPhysicalDeviceFeatures2(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR")); + } + + if (functions.GetPhysicalDeviceProperties2 == nullptr) { + functions.GetPhysicalDeviceProperties2 = PFN_vkGetPhysicalDeviceProperties2(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR")); + } + } + + // Device. + functions.GetDeviceProcAddr = PFN_vkGetDeviceProcAddr(vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); + + // Surfaces. + functions.GetPhysicalDeviceSurfaceSupportKHR = PFN_vkGetPhysicalDeviceSurfaceSupportKHR(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR")); + functions.GetPhysicalDeviceSurfaceFormatsKHR = PFN_vkGetPhysicalDeviceSurfaceFormatsKHR(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR")); + functions.GetPhysicalDeviceSurfaceCapabilitiesKHR = PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + functions.GetPhysicalDeviceSurfacePresentModesKHR = PFN_vkGetPhysicalDeviceSurfacePresentModesKHR(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR")); + + // Debug utils and report. + if (has_debug_utils_extension) { + // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). + functions.CreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + functions.DestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + functions.CmdBeginDebugUtilsLabelEXT = (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkCmdBeginDebugUtilsLabelEXT"); + functions.CmdEndDebugUtilsLabelEXT = (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkCmdEndDebugUtilsLabelEXT"); + functions.SetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT"); + + if (!functions.debug_util_functions_available()) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "GetProcAddr: Failed to init VK_EXT_debug_utils\nGetProcAddr: Failure"); + } + + VkResult res = functions.CreateDebugUtilsMessengerEXT(instance, &debug_messenger_create_info, nullptr, &debug_messenger); + switch (res) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CreateDebugUtilsMessengerEXT: out of host memory\nCreateDebugUtilsMessengerEXT Failure"); + break; + default: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CreateDebugUtilsMessengerEXT: unknown failure\nCreateDebugUtilsMessengerEXT Failure"); + break; + } + } else if (has_debug_report_extension) { + functions.CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); + functions.DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT"); + functions.DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); + + if (!functions.debug_report_functions_available()) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "GetProcAddr: Failed to init VK_EXT_debug_report\nGetProcAddr: Failure"); + } + + VkResult res = functions.CreateDebugReportCallbackEXT(instance, &debug_report_callback_create_info, nullptr, &debug_report); + switch (res) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CreateDebugReportCallbackEXT: out of host memory\nCreateDebugReportCallbackEXT Failure"); + break; + default: + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CreateDebugReportCallbackEXT: unknown failure\nCreateDebugReportCallbackEXT Failure"); + break; + } + } + + return OK; +} + +Error RenderingContextDriverVulkan::_initialize_devices() { + if (VulkanHooks::get_singleton() != nullptr) { + VkPhysicalDevice physical_device; + bool device_retrieved = VulkanHooks::get_singleton()->get_physical_device(&physical_device); + ERR_FAIL_COND_V(!device_retrieved, ERR_CANT_CREATE); + + // When a hook is active, pretend the device returned by the hook is the only device available. + driver_devices.resize(1); + physical_devices.resize(1); + device_queue_families.resize(1); + physical_devices[0] = physical_device; + + } else { + uint32_t physical_device_count = 0; + VkResult err = vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(physical_device_count == 0, ERR_CANT_CREATE, "vkEnumeratePhysicalDevices reported zero accessible devices.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\nvkEnumeratePhysicalDevices Failure."); + + driver_devices.resize(physical_device_count); + physical_devices.resize(physical_device_count); + device_queue_families.resize(physical_device_count); + err = vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + } + + // Fill the list of driver devices with the properties from the physical devices. + for (uint32_t i = 0; i < physical_devices.size(); i++) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(physical_devices[i], &props); + + Device &driver_device = driver_devices[i]; + driver_device.name = String::utf8(props.deviceName); + driver_device.vendor = Vendor(props.vendorID); + driver_device.type = DeviceType(props.deviceType); + + uint32_t queue_family_properties_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &queue_family_properties_count, nullptr); + + if (queue_family_properties_count > 0) { + device_queue_families[i].properties.resize(queue_family_properties_count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &queue_family_properties_count, device_queue_families[i].properties.ptr()); + } + } + + return OK; +} + +bool RenderingContextDriverVulkan::_use_validation_layers() const { + return Engine::get_singleton()->is_validation_layers_enabled(); +} + +Error RenderingContextDriverVulkan::_create_vulkan_instance(const VkInstanceCreateInfo *p_create_info, VkInstance *r_instance) { + if (VulkanHooks::get_singleton() != nullptr) { + return VulkanHooks::get_singleton()->create_vulkan_instance(p_create_info, r_instance) ? OK : ERR_CANT_CREATE; + } else { + VkResult err = vkCreateInstance(p_create_info, nullptr, r_instance); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, + "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, + "Cannot find a specified extension library.\n" + "Make sure your layers path is set appropriately.\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, + "vkCreateInstance failed.\n\n" + "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" + "Please look at the Getting Started guide for additional information.\n" + "vkCreateInstance Failure"); + } + + return OK; +} + +Error RenderingContextDriverVulkan::initialize() { + Error err; + +#ifdef USE_VOLK + if (volkInitialize() != VK_SUCCESS) { + return FAILED; + } +#endif + + err = _initialize_vulkan_version(); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_instance_extensions(); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_instance(); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_devices(); + ERR_FAIL_COND_V(err != OK, err); + + return OK; +} + +const RenderingContextDriver::Device &RenderingContextDriverVulkan::device_get(uint32_t p_device_index) const { + DEV_ASSERT(p_device_index < driver_devices.size()); + return driver_devices[p_device_index]; +} + +uint32_t RenderingContextDriverVulkan::device_get_count() const { + return driver_devices.size(); +} + +bool RenderingContextDriverVulkan::device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const { + DEV_ASSERT(p_device_index < physical_devices.size()); + + // Check if any of the queues supported by the device supports presenting to the window's surface. + const VkPhysicalDevice physical_device = physical_devices[p_device_index]; + const DeviceQueueFamilies &queue_families = device_queue_families[p_device_index]; + for (uint32_t i = 0; i < queue_families.properties.size(); i++) { + if ((queue_families.properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && queue_family_supports_present(physical_device, i, p_surface)) { + return true; + } + } + + return false; +} + +RenderingDeviceDriver *RenderingContextDriverVulkan::driver_create() { + return memnew(RenderingDeviceDriverVulkan(this)); +} + +void RenderingContextDriverVulkan::driver_free(RenderingDeviceDriver *p_driver) { + memdelete(p_driver); +} + +RenderingContextDriver::SurfaceID RenderingContextDriverVulkan::surface_create(const void *p_platform_data) { + DEV_ASSERT(false && "Surface creation should not be called on the platform-agnostic version of the driver."); + return SurfaceID(); +} + +void RenderingContextDriverVulkan::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) { + Surface *surface = (Surface *)(p_surface); + surface->width = p_width; + surface->height = p_height; + surface->needs_resize = true; +} + +void RenderingContextDriverVulkan::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) { + Surface *surface = (Surface *)(p_surface); + surface->vsync_mode = p_vsync_mode; + surface->needs_resize = true; +} + +DisplayServer::VSyncMode RenderingContextDriverVulkan::surface_get_vsync_mode(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->vsync_mode; +} + +uint32_t RenderingContextDriverVulkan::surface_get_width(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->width; +} + +uint32_t RenderingContextDriverVulkan::surface_get_height(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->height; +} + +void RenderingContextDriverVulkan::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) { + Surface *surface = (Surface *)(p_surface); + surface->needs_resize = p_needs_resize; +} + +bool RenderingContextDriverVulkan::surface_get_needs_resize(SurfaceID p_surface) const { + Surface *surface = (Surface *)(p_surface); + return surface->needs_resize; +} + +void RenderingContextDriverVulkan::surface_destroy(SurfaceID p_surface) { + Surface *surface = (Surface *)(p_surface); + vkDestroySurfaceKHR(instance, surface->vk_surface, nullptr); + memdelete(surface); +} + +bool RenderingContextDriverVulkan::is_debug_utils_enabled() const { + return enabled_instance_extension_names.has(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +} + +VkInstance RenderingContextDriverVulkan::instance_get() const { + return instance; +} + +VkPhysicalDevice RenderingContextDriverVulkan::physical_device_get(uint32_t p_device_index) const { + DEV_ASSERT(p_device_index < physical_devices.size()); + return physical_devices[p_device_index]; +} + +uint32_t RenderingContextDriverVulkan::queue_family_get_count(uint32_t p_device_index) const { + DEV_ASSERT(p_device_index < physical_devices.size()); + return device_queue_families[p_device_index].properties.size(); +} + +VkQueueFamilyProperties RenderingContextDriverVulkan::queue_family_get(uint32_t p_device_index, uint32_t p_queue_family_index) const { + DEV_ASSERT(p_device_index < physical_devices.size()); + DEV_ASSERT(p_queue_family_index < queue_family_get_count(p_device_index)); + return device_queue_families[p_device_index].properties[p_queue_family_index]; +} + +bool RenderingContextDriverVulkan::queue_family_supports_present(VkPhysicalDevice p_physical_device, uint32_t p_queue_family_index, SurfaceID p_surface) const { + DEV_ASSERT(p_physical_device != VK_NULL_HANDLE); + DEV_ASSERT(p_surface != 0); + Surface *surface = (Surface *)(p_surface); + VkBool32 present_supported = false; + VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(p_physical_device, p_queue_family_index, surface->vk_surface, &present_supported); + return err == VK_SUCCESS && present_supported; +} + +const RenderingContextDriverVulkan::Functions &RenderingContextDriverVulkan::functions_get() const { + return functions; +} + +#endif // VULKAN_ENABLED diff --git a/drivers/vulkan/rendering_context_driver_vulkan.h b/drivers/vulkan/rendering_context_driver_vulkan.h new file mode 100644 index 00000000000..6348f90d55e --- /dev/null +++ b/drivers/vulkan/rendering_context_driver_vulkan.h @@ -0,0 +1,161 @@ +/**************************************************************************/ +/* rendering_context_driver_vulkan.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. */ +/**************************************************************************/ + +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_H + +#ifdef VULKAN_ENABLED + +#include "servers/rendering/rendering_context_driver.h" + +#ifdef USE_VOLK +#include +#else +#include +#endif + +class RenderingContextDriverVulkan : public RenderingContextDriver { +public: + struct Functions { + // Physical device. + PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2 = nullptr; + PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2 = nullptr; + + // Device. + PFN_vkGetDeviceProcAddr GetDeviceProcAddr = nullptr; + + // Surfaces. + PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR = nullptr; + + // Debug utils. + PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT = nullptr; + PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT = nullptr; + PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT = nullptr; + PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT = nullptr; + PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT = nullptr; + + bool debug_util_functions_available() const { + return CreateDebugUtilsMessengerEXT != nullptr && + DestroyDebugUtilsMessengerEXT != nullptr && + CmdBeginDebugUtilsLabelEXT != nullptr && + CmdEndDebugUtilsLabelEXT != nullptr && + SetDebugUtilsObjectNameEXT != nullptr; + } + + // Debug report. + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; + + bool debug_report_functions_available() const { + return CreateDebugReportCallbackEXT != nullptr && + DebugReportMessageEXT != nullptr && + DestroyDebugReportCallbackEXT != nullptr; + } + }; + +private: + struct DeviceQueueFamilies { + TightLocalVector properties; + }; + + VkInstance instance = VK_NULL_HANDLE; + uint32_t instance_api_version = VK_API_VERSION_1_0; + HashMap requested_instance_extensions; + HashSet enabled_instance_extension_names; + TightLocalVector driver_devices; + TightLocalVector physical_devices; + TightLocalVector device_queue_families; + VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE; + VkDebugReportCallbackEXT debug_report = VK_NULL_HANDLE; + Functions functions; + + Error _initialize_vulkan_version(); + void _register_requested_instance_extension(const CharString &p_extension_name, bool p_required); + Error _initialize_instance_extensions(); + Error _initialize_instance(); + Error _initialize_devices(); + + // Static callbacks. + static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, VkDebugUtilsMessageTypeFlagsEXT p_message_type, const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data); + static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback(VkDebugReportFlagsEXT p_flags, VkDebugReportObjectTypeEXT p_object_type, uint64_t p_object, size_t p_location, int32_t p_message_code, const char *p_layer_prefix, const char *p_message, void *p_user_data); + +protected: + Error _find_validation_layers(TightLocalVector &r_layer_names) const; + + // Can be overridden by platform-specific drivers. + virtual const char *_get_platform_surface_extension() const { return nullptr; } + virtual bool _use_validation_layers() const; + virtual Error _create_vulkan_instance(const VkInstanceCreateInfo *p_create_info, VkInstance *r_instance); + +public: + virtual Error initialize() override; + virtual const Device &device_get(uint32_t p_device_index) const override; + virtual uint32_t device_get_count() const override; + virtual bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const override; + virtual RenderingDeviceDriver *driver_create() override; + virtual void driver_free(RenderingDeviceDriver *p_driver) override; + virtual SurfaceID surface_create(const void *p_platform_data) override; + virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override; + virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override; + virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override; + virtual uint32_t surface_get_width(SurfaceID p_surface) const override; + virtual uint32_t surface_get_height(SurfaceID p_surface) const override; + virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override; + virtual bool surface_get_needs_resize(SurfaceID p_surface) const override; + virtual void surface_destroy(SurfaceID p_surface) override; + virtual bool is_debug_utils_enabled() const override; + + // Vulkan-only methods. + struct Surface { + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + uint32_t width = 0; + uint32_t height = 0; + DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; + bool needs_resize = false; + }; + + VkInstance instance_get() const; + VkPhysicalDevice physical_device_get(uint32_t p_device_index) const; + uint32_t queue_family_get_count(uint32_t p_device_index) const; + VkQueueFamilyProperties queue_family_get(uint32_t p_device_index, uint32_t p_queue_family_index) const; + bool queue_family_supports_present(VkPhysicalDevice p_physical_device, uint32_t p_queue_family_index, SurfaceID p_surface) const; + const Functions &functions_get() const; + + RenderingContextDriverVulkan(); + virtual ~RenderingContextDriverVulkan() override; +}; + +#endif // VULKAN_ENABLED + +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_H diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 45137407a28..1eab2922d15 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -33,7 +33,9 @@ #include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "thirdparty/misc/smolv.h" -#include "vulkan_context.h" +#include "vulkan_hooks.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define PRINT_NATIVE_COMMANDS 0 @@ -274,6 +276,883 @@ static_assert(ENUM_MEMBERS_EQUAL(RDD::COMPARE_OP_ALWAYS, VK_COMPARE_OP_ALWAYS)); static_assert(ARRAYS_COMPATIBLE_FIELDWISE(Rect2i, VkRect2D)); +uint32_t RenderingDeviceDriverVulkan::SubgroupCapabilities::supported_stages_flags_rd() const { + uint32_t flags = 0; + + if (supported_stages & VK_SHADER_STAGE_VERTEX_BIT) { + flags += SHADER_STAGE_VERTEX_BIT; + } + if (supported_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + flags += SHADER_STAGE_TESSELATION_CONTROL_BIT; + } + if (supported_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + flags += SHADER_STAGE_TESSELATION_EVALUATION_BIT; + } + if (supported_stages & VK_SHADER_STAGE_GEOMETRY_BIT) { + // FIXME: Add shader stage geometry bit. + } + if (supported_stages & VK_SHADER_STAGE_FRAGMENT_BIT) { + flags += SHADER_STAGE_FRAGMENT_BIT; + } + if (supported_stages & VK_SHADER_STAGE_COMPUTE_BIT) { + flags += SHADER_STAGE_COMPUTE_BIT; + } + + return flags; +} + +String RenderingDeviceDriverVulkan::SubgroupCapabilities::supported_stages_desc() const { + String res; + + if (supported_stages & VK_SHADER_STAGE_VERTEX_BIT) { + res += ", STAGE_VERTEX"; + } + if (supported_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { + res += ", STAGE_TESSELLATION_CONTROL"; + } + if (supported_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { + res += ", STAGE_TESSELLATION_EVALUATION"; + } + if (supported_stages & VK_SHADER_STAGE_GEOMETRY_BIT) { + res += ", STAGE_GEOMETRY"; + } + if (supported_stages & VK_SHADER_STAGE_FRAGMENT_BIT) { + res += ", STAGE_FRAGMENT"; + } + if (supported_stages & VK_SHADER_STAGE_COMPUTE_BIT) { + res += ", STAGE_COMPUTE"; + } + + // These are not defined on Android GRMBL. + if (supported_stages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { + res += ", STAGE_RAYGEN_KHR"; + } + if (supported_stages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { + res += ", STAGE_ANY_HIT_KHR"; + } + if (supported_stages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { + res += ", STAGE_CLOSEST_HIT_KHR"; + } + if (supported_stages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { + res += ", STAGE_MISS_KHR"; + } + if (supported_stages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { + res += ", STAGE_INTERSECTION_KHR"; + } + if (supported_stages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { + res += ", STAGE_CALLABLE_KHR"; + } + if (supported_stages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { + res += ", STAGE_TASK_NV"; + } + if (supported_stages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { + res += ", STAGE_MESH_NV"; + } + + return res.substr(2); // Remove first ", ". +} + +uint32_t RenderingDeviceDriverVulkan::SubgroupCapabilities::supported_operations_flags_rd() const { + uint32_t flags = 0; + + if (supported_operations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + flags += SUBGROUP_BASIC_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + flags += SUBGROUP_VOTE_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + flags += SUBGROUP_ARITHMETIC_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + flags += SUBGROUP_BALLOT_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + flags += SUBGROUP_SHUFFLE_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + flags += SUBGROUP_SHUFFLE_RELATIVE_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + flags += SUBGROUP_CLUSTERED_BIT; + } + if (supported_operations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + flags += SUBGROUP_QUAD_BIT; + } + + return flags; +} + +String RenderingDeviceDriverVulkan::SubgroupCapabilities::supported_operations_desc() const { + String res; + + if (supported_operations & VK_SUBGROUP_FEATURE_BASIC_BIT) { + res += ", FEATURE_BASIC"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_VOTE_BIT) { + res += ", FEATURE_VOTE"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { + res += ", FEATURE_ARITHMETIC"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { + res += ", FEATURE_BALLOT"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { + res += ", FEATURE_SHUFFLE"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { + res += ", FEATURE_SHUFFLE_RELATIVE"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { + res += ", FEATURE_CLUSTERED"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_QUAD_BIT) { + res += ", FEATURE_QUAD"; + } + if (supported_operations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { + res += ", FEATURE_PARTITIONED_NV"; + } + + return res.substr(2); // Remove first ", ". +} + +/*****************/ +/**** GENERIC ****/ +/*****************/ + +void RenderingDeviceDriverVulkan::_register_requested_device_extension(const CharString &p_extension_name, bool p_required) { + ERR_FAIL_COND(requested_device_extensions.has(p_extension_name)); + requested_device_extensions[p_extension_name] = p_required; +} + +Error RenderingDeviceDriverVulkan::_initialize_device_extensions() { + enabled_device_extension_names.clear(); + + _register_requested_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); + _register_requested_device_extension(VK_KHR_MULTIVIEW_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); + _register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); + _register_requested_device_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, false); + _register_requested_device_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); + + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + _register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); + } + + uint32_t device_extension_count = 0; + VkResult err = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(device_extension_count == 0, ERR_CANT_CREATE, "vkEnumerateDeviceExtensionProperties failed to find any extensions\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?"); + + TightLocalVector device_extensions; + device_extensions.resize(device_extension_count); + err = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, device_extensions.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + +#ifdef DEV_ENABLED + for (uint32_t i = 0; i < device_extension_count; i++) { + print_verbose(String("VULKAN: Found device extension ") + String::utf8(device_extensions[i].extensionName)); + } +#endif + + // Enable all extensions that are supported and requested. + for (uint32_t i = 0; i < device_extension_count; i++) { + CharString extension_name(device_extensions[i].extensionName); + if (requested_device_extensions.has(extension_name)) { + enabled_device_extension_names.insert(extension_name); + } + } + + // Now check our requested extensions. + for (KeyValue &requested_extension : requested_device_extensions) { + if (!enabled_device_extension_names.has(requested_extension.key)) { + if (requested_extension.value) { + ERR_FAIL_V_MSG(ERR_BUG, String("Required extension ") + String::utf8(requested_extension.key) + String(" not found.")); + } else { + print_verbose(String("Optional extension ") + String::utf8(requested_extension.key) + String(" not found")); + } + } + } + + return OK; +} + +Error RenderingDeviceDriverVulkan::_check_device_features() { + vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features); + + // Check for required features. + if (!physical_device_features.imageCubeArray || !physical_device_features.independentBlend) { + String error_string = vformat("Your GPU (%s) does not support the following features which are required to use Vulkan-based renderers in Godot:\n\n", context_device.name); + if (!physical_device_features.imageCubeArray) { + error_string += "- No support for image cube arrays.\n"; + } + if (!physical_device_features.independentBlend) { + error_string += "- No support for independentBlend.\n"; + } + error_string += "\nThis is usually a hardware limitation, so updating graphics drivers won't help in most cases."; + +#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) + // Android/iOS platform ports currently don't exit themselves when this method returns `ERR_CANT_CREATE`. + OS::get_singleton()->alert(error_string + "\nClick OK to exit (black screen will be visible)."); +#else + OS::get_singleton()->alert(error_string + "\nClick OK to exit."); +#endif + + return ERR_CANT_CREATE; + } + + // Opt-in to the features we actually need/use. These can be changed in the future. + // We do this for multiple reasons: + // + // 1. Certain features (like sparse* stuff) cause unnecessary internal driver allocations. + // 2. Others like shaderStorageImageMultisample are a huge red flag + // (MSAA + Storage is rarely needed). + // 3. Most features when turned off aren't actually off (we just promise the driver not to use them) + // and it is validation what will complain. This allows us to target a minimum baseline. + // + // TODO: Allow the user to override these settings (i.e. turn off more stuff) using profiles + // so they can target a broad range of HW. For example Mali HW does not have + // shaderClipDistance/shaderCullDistance; thus validation would complain if such feature is used; + // allowing them to fix the problem without even owning Mali HW to test on. + // + // The excluded features are: + // - robustBufferAccess (can hamper performance on some hardware) + // - occlusionQueryPrecise + // - pipelineStatisticsQuery + // - shaderStorageImageMultisample (unsupported by Intel Arc, prevents from using MSAA storage accidentally) + // - shaderResourceResidency + // - sparseBinding (we don't use sparse features and enabling them cause extra internal allocations inside the Vulkan driver we don't need) + // - sparseResidencyBuffer + // - sparseResidencyImage2D + // - sparseResidencyImage3D + // - sparseResidency2Samples + // - sparseResidency4Samples + // - sparseResidency8Samples + // - sparseResidency16Samples + // - sparseResidencyAliased + // - inheritedQueries + +#define VK_DEVICEFEATURE_ENABLE_IF(x) \ + if (physical_device_features.x) { \ + requested_device_features.x = physical_device_features.x; \ + } else \ + ((void)0) + + requested_device_features = {}; + VK_DEVICEFEATURE_ENABLE_IF(fullDrawIndexUint32); + VK_DEVICEFEATURE_ENABLE_IF(imageCubeArray); + VK_DEVICEFEATURE_ENABLE_IF(independentBlend); + VK_DEVICEFEATURE_ENABLE_IF(geometryShader); + VK_DEVICEFEATURE_ENABLE_IF(tessellationShader); + VK_DEVICEFEATURE_ENABLE_IF(sampleRateShading); + VK_DEVICEFEATURE_ENABLE_IF(dualSrcBlend); + VK_DEVICEFEATURE_ENABLE_IF(logicOp); + VK_DEVICEFEATURE_ENABLE_IF(multiDrawIndirect); + VK_DEVICEFEATURE_ENABLE_IF(drawIndirectFirstInstance); + VK_DEVICEFEATURE_ENABLE_IF(depthClamp); + VK_DEVICEFEATURE_ENABLE_IF(depthBiasClamp); + VK_DEVICEFEATURE_ENABLE_IF(fillModeNonSolid); + VK_DEVICEFEATURE_ENABLE_IF(depthBounds); + VK_DEVICEFEATURE_ENABLE_IF(wideLines); + VK_DEVICEFEATURE_ENABLE_IF(largePoints); + VK_DEVICEFEATURE_ENABLE_IF(alphaToOne); + VK_DEVICEFEATURE_ENABLE_IF(multiViewport); + VK_DEVICEFEATURE_ENABLE_IF(samplerAnisotropy); + VK_DEVICEFEATURE_ENABLE_IF(textureCompressionETC2); + VK_DEVICEFEATURE_ENABLE_IF(textureCompressionASTC_LDR); + VK_DEVICEFEATURE_ENABLE_IF(textureCompressionBC); + VK_DEVICEFEATURE_ENABLE_IF(vertexPipelineStoresAndAtomics); + VK_DEVICEFEATURE_ENABLE_IF(fragmentStoresAndAtomics); + VK_DEVICEFEATURE_ENABLE_IF(shaderTessellationAndGeometryPointSize); + VK_DEVICEFEATURE_ENABLE_IF(shaderImageGatherExtended); + VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageExtendedFormats); + VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageReadWithoutFormat); + VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageWriteWithoutFormat); + VK_DEVICEFEATURE_ENABLE_IF(shaderUniformBufferArrayDynamicIndexing); + VK_DEVICEFEATURE_ENABLE_IF(shaderSampledImageArrayDynamicIndexing); + VK_DEVICEFEATURE_ENABLE_IF(shaderStorageBufferArrayDynamicIndexing); + VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageArrayDynamicIndexing); + VK_DEVICEFEATURE_ENABLE_IF(shaderClipDistance); + VK_DEVICEFEATURE_ENABLE_IF(shaderCullDistance); + VK_DEVICEFEATURE_ENABLE_IF(shaderFloat64); + VK_DEVICEFEATURE_ENABLE_IF(shaderInt64); + VK_DEVICEFEATURE_ENABLE_IF(shaderInt16); + VK_DEVICEFEATURE_ENABLE_IF(shaderResourceMinLod); + VK_DEVICEFEATURE_ENABLE_IF(variableMultisampleRate); + + return OK; +} + +Error RenderingDeviceDriverVulkan::_check_device_capabilities() { + // Fill device family and version. + device_capabilities.device_family = DEVICE_VULKAN; + device_capabilities.version_major = VK_API_VERSION_MAJOR(physical_device_properties.apiVersion); + device_capabilities.version_minor = VK_API_VERSION_MINOR(physical_device_properties.apiVersion); + + // References: + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html + // https://www.khronos.org/blog/vulkan-subgroup-tutorial + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + if (functions.GetPhysicalDeviceFeatures2 != nullptr) { + // We must check that the corresponding extension is present before assuming a feature as enabled. + // See also: https://github.com/godotengine/godot/issues/65409 + + void *next_features = nullptr; + VkPhysicalDeviceVulkan12Features device_features_vk_1_2 = {}; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {}; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; + VkPhysicalDeviceMultiviewFeatures multiview_features = {}; + VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {}; + + const bool use_1_2_features = physical_device_properties.apiVersion >= VK_API_VERSION_1_2; + if (use_1_2_features) { + device_features_vk_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + device_features_vk_1_2.pNext = next_features; + next_features = &device_features_vk_1_2; + } else if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { + shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; + shader_features.pNext = next_features; + next_features = &shader_features; + } + + if (enabled_device_extension_names.has(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { + vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; + vrs_features.pNext = next_features; + next_features = &vrs_features; + } + + if (enabled_device_extension_names.has(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { + storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; + storage_feature.pNext = next_features; + next_features = &storage_feature; + } + + if (enabled_device_extension_names.has(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = next_features; + next_features = &multiview_features; + } + + if (enabled_device_extension_names.has(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) { + pipeline_cache_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES; + pipeline_cache_control_features.pNext = next_features; + next_features = &pipeline_cache_control_features; + } + + VkPhysicalDeviceFeatures2 device_features_2 = {}; + device_features_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + device_features_2.pNext = next_features; + functions.GetPhysicalDeviceFeatures2(physical_device, &device_features_2); + + if (use_1_2_features) { +#ifdef MACOS_ENABLED + ERR_FAIL_COND_V_MSG(!device_features_vk_1_2.shaderSampledImageArrayNonUniformIndexing, ERR_CANT_CREATE, "Your GPU doesn't support shaderSampledImageArrayNonUniformIndexing which is required to use the Vulkan-based renderers in Godot."); +#endif + if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { + shader_capabilities.shader_float16_is_supported = device_features_vk_1_2.shaderFloat16; + shader_capabilities.shader_int8_is_supported = device_features_vk_1_2.shaderInt8; + } + } else { + if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { + shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; + shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + } + } + + if (enabled_device_extension_names.has(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { + vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; + vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; + vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; + } + + if (enabled_device_extension_names.has(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { + multiview_capabilities.is_supported = multiview_features.multiview; + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + } + + if (enabled_device_extension_names.has(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; + storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; + storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; + } + + if (enabled_device_extension_names.has(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) { + pipeline_cache_control_support = pipeline_cache_control_features.pipelineCreationCacheControl; + } + } + + if (functions.GetPhysicalDeviceProperties2 != nullptr) { + void *next_properties = nullptr; + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrs_properties = {}; + VkPhysicalDeviceMultiviewProperties multiview_properties = {}; + VkPhysicalDeviceSubgroupProperties subgroup_properties = {}; + VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control_properties = {}; + VkPhysicalDeviceProperties2 physical_device_properties_2 = {}; + + const bool use_1_1_properties = physical_device_properties.apiVersion >= VK_API_VERSION_1_1; + if (use_1_1_properties) { + subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroup_properties.pNext = next_properties; + next_properties = &subgroup_properties; + + subgroup_capabilities.size_control_is_supported = enabled_device_extension_names.has(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); + if (subgroup_capabilities.size_control_is_supported) { + subgroup_size_control_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; + subgroup_size_control_properties.pNext = next_properties; + next_properties = &subgroup_size_control_properties; + } + } + + if (multiview_capabilities.is_supported) { + multiview_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; + multiview_properties.pNext = next_properties; + next_properties = &multiview_properties; + } + + if (vrs_capabilities.attachment_vrs_supported) { + vrs_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; + vrs_properties.pNext = next_properties; + next_properties = &vrs_properties; + } + + physical_device_properties_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physical_device_properties_2.pNext = next_properties; + functions.GetPhysicalDeviceProperties2(physical_device, &physical_device_properties_2); + + subgroup_capabilities.size = subgroup_properties.subgroupSize; + subgroup_capabilities.min_size = subgroup_properties.subgroupSize; + subgroup_capabilities.max_size = subgroup_properties.subgroupSize; + subgroup_capabilities.supported_stages = subgroup_properties.supportedStages; + subgroup_capabilities.supported_operations = subgroup_properties.supportedOperations; + + // Note: quadOperationsInAllStages will be true if: + // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT. + // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT. + subgroup_capabilities.quad_operations_in_all_stages = subgroup_properties.quadOperationsInAllStages; + + if (subgroup_capabilities.size_control_is_supported && (subgroup_size_control_properties.requiredSubgroupSizeStages & VK_SHADER_STAGE_COMPUTE_BIT)) { + subgroup_capabilities.min_size = subgroup_size_control_properties.minSubgroupSize; + subgroup_capabilities.max_size = subgroup_size_control_properties.maxSubgroupSize; + } + + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + print_verbose("- Vulkan Variable Rate Shading supported:"); + if (vrs_capabilities.pipeline_vrs_supported) { + print_verbose(" Pipeline fragment shading rate"); + } + if (vrs_capabilities.primitive_vrs_supported) { + print_verbose(" Primitive fragment shading rate"); + } + if (vrs_capabilities.attachment_vrs_supported) { + // TODO: Expose these somehow to the end user. + vrs_capabilities.min_texel_size.x = vrs_properties.minFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.min_texel_size.y = vrs_properties.minFragmentShadingRateAttachmentTexelSize.height; + vrs_capabilities.max_texel_size.x = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.max_texel_size.y = vrs_properties.maxFragmentShadingRateAttachmentTexelSize.height; + + // We'll attempt to default to a texel size of 16x16. + vrs_capabilities.texel_size.x = CLAMP(16, vrs_capabilities.min_texel_size.x, vrs_capabilities.max_texel_size.x); + vrs_capabilities.texel_size.y = CLAMP(16, vrs_capabilities.min_texel_size.y, vrs_capabilities.max_texel_size.y); + + print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + } + + } else { + print_verbose("- Vulkan Variable Rate Shading not supported"); + } + + if (multiview_capabilities.is_supported) { + multiview_capabilities.max_view_count = multiview_properties.maxMultiviewViewCount; + multiview_capabilities.max_instance_count = multiview_properties.maxMultiviewInstanceIndex; + + print_verbose("- Vulkan multiview supported:"); + print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); + print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); + } else { + print_verbose("- Vulkan multiview not supported"); + } + + print_verbose("- Vulkan subgroup:"); + print_verbose(" size: " + itos(subgroup_capabilities.size)); + print_verbose(" min size: " + itos(subgroup_capabilities.min_size)); + print_verbose(" max size: " + itos(subgroup_capabilities.max_size)); + print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); + print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); + if (subgroup_capabilities.quad_operations_in_all_stages) { + print_verbose(" quad operations in all stages"); + } + } + + return OK; +} + +Error RenderingDeviceDriverVulkan::_add_queue_create_info(LocalVector &r_queue_create_info) { + uint32_t queue_family_count = queue_family_properties.size(); + queue_families.resize(queue_family_count); + + VkQueueFlags queue_flags_mask = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; + const uint32_t max_queue_count_per_family = 1; + static const float queue_priorities[max_queue_count_per_family] = {}; + for (uint32_t i = 0; i < queue_family_count; i++) { + if ((queue_family_properties[i].queueFlags & queue_flags_mask) == 0) { + // We ignore creating queues in families that don't support any of the operations we require. + continue; + } + + VkDeviceQueueCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + create_info.queueFamilyIndex = i; + create_info.queueCount = MIN(queue_family_properties[i].queueCount, max_queue_count_per_family); + create_info.pQueuePriorities = queue_priorities; + r_queue_create_info.push_back(create_info); + + // Prepare the vectors where the queues will be filled out. + queue_families[i].resize(create_info.queueCount); + } + + return OK; +} + +Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector &p_queue_create_info) { + TightLocalVector enabled_extension_names; + enabled_extension_names.reserve(enabled_device_extension_names.size()); + for (const CharString &extension_name : enabled_device_extension_names) { + enabled_extension_names.push_back(extension_name.ptr()); + } + + void *create_info_next = nullptr; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {}; + shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; + shader_features.pNext = create_info_next; + shader_features.shaderFloat16 = shader_capabilities.shader_float16_is_supported; + shader_features.shaderInt8 = shader_capabilities.shader_int8_is_supported; + create_info_next = &shader_features; + + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; + vrs_features.pNext = create_info_next; + vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; + vrs_features.primitiveFragmentShadingRate = vrs_capabilities.primitive_vrs_supported; + vrs_features.attachmentFragmentShadingRate = vrs_capabilities.attachment_vrs_supported; + create_info_next = &vrs_features; + } + + VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {}; + if (pipeline_cache_control_support) { + pipeline_cache_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES; + pipeline_cache_control_features.pNext = create_info_next; + pipeline_cache_control_features.pipelineCreationCacheControl = pipeline_cache_control_support; + create_info_next = &pipeline_cache_control_features; + } + + VkPhysicalDeviceVulkan11Features vulkan_1_1_features = {}; + VkPhysicalDevice16BitStorageFeaturesKHR storage_features = {}; + VkPhysicalDeviceMultiviewFeatures multiview_features = {}; + const bool enable_1_2_features = physical_device_properties.apiVersion >= VK_API_VERSION_1_2; + if (enable_1_2_features) { + // In Vulkan 1.2 and newer we use a newer struct to enable various features. + vulkan_1_1_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vulkan_1_1_features.pNext = create_info_next; + vulkan_1_1_features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + vulkan_1_1_features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + vulkan_1_1_features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + vulkan_1_1_features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + vulkan_1_1_features.multiview = multiview_capabilities.is_supported; + vulkan_1_1_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + vulkan_1_1_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + vulkan_1_1_features.variablePointersStorageBuffer = 0; + vulkan_1_1_features.variablePointers = 0; + vulkan_1_1_features.protectedMemory = 0; + vulkan_1_1_features.samplerYcbcrConversion = 0; + vulkan_1_1_features.shaderDrawParameters = 0; + create_info_next = &vulkan_1_1_features; + } else { + // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. + storage_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; + storage_features.pNext = create_info_next; + storage_features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + storage_features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + storage_features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + storage_features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + create_info_next = &storage_features; + + const bool enable_1_1_features = physical_device_properties.apiVersion >= VK_API_VERSION_1_1; + if (enable_1_1_features) { + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = create_info_next; + multiview_features.multiview = multiview_capabilities.is_supported; + multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + create_info_next = &multiview_features; + } + } + + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.pNext = create_info_next; + create_info.queueCreateInfoCount = p_queue_create_info.size(); + create_info.pQueueCreateInfos = p_queue_create_info.ptr(); + create_info.enabledExtensionCount = enabled_extension_names.size(); + create_info.ppEnabledExtensionNames = enabled_extension_names.ptr(); + create_info.pEnabledFeatures = &requested_device_features; + + if (VulkanHooks::get_singleton() != nullptr) { + bool device_created = VulkanHooks::get_singleton()->create_vulkan_device(&create_info, &vk_device); + ERR_FAIL_COND_V(!device_created, ERR_CANT_CREATE); + } else { + VkResult err = vkCreateDevice(physical_device, &create_info, nullptr, &vk_device); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + } + + for (uint32_t i = 0; i < queue_families.size(); i++) { + for (uint32_t j = 0; j < queue_families[i].size(); j++) { + vkGetDeviceQueue(vk_device, i, j, &queue_families[i][j].queue); + } + } + + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + if (functions.GetDeviceProcAddr != nullptr) { + device_functions.CreateSwapchainKHR = PFN_vkCreateSwapchainKHR(functions.GetDeviceProcAddr(vk_device, "vkCreateSwapchainKHR")); + device_functions.DestroySwapchainKHR = PFN_vkDestroySwapchainKHR(functions.GetDeviceProcAddr(vk_device, "vkDestroySwapchainKHR")); + device_functions.GetSwapchainImagesKHR = PFN_vkGetSwapchainImagesKHR(functions.GetDeviceProcAddr(vk_device, "vkGetSwapchainImagesKHR")); + device_functions.AcquireNextImageKHR = PFN_vkAcquireNextImageKHR(functions.GetDeviceProcAddr(vk_device, "vkAcquireNextImageKHR")); + device_functions.QueuePresentKHR = PFN_vkQueuePresentKHR(functions.GetDeviceProcAddr(vk_device, "vkQueuePresentKHR")); + + if (enabled_device_extension_names.has(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { + device_functions.CreateRenderPass2KHR = PFN_vkCreateRenderPass2KHR(functions.GetDeviceProcAddr(vk_device, "vkCreateRenderPass2KHR")); + } + } + + return OK; +} + +Error RenderingDeviceDriverVulkan::_initialize_allocator() { + VmaAllocatorCreateInfo allocator_info = {}; + allocator_info.physicalDevice = physical_device; + allocator_info.device = vk_device; + allocator_info.instance = context_driver->instance_get(); + VkResult err = vmaCreateAllocator(&allocator_info, &allocator); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vmaCreateAllocator failed with error " + itos(err) + "."); + + return OK; +} + +Error RenderingDeviceDriverVulkan::_initialize_pipeline_cache() { + pipelines_cache.buffer.resize(sizeof(PipelineCacheHeader)); + PipelineCacheHeader *header = (PipelineCacheHeader *)(pipelines_cache.buffer.ptrw()); + *header = {}; + header->magic = 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE; + header->device_id = physical_device_properties.deviceID; + header->vendor_id = physical_device_properties.vendorID; + header->driver_version = physical_device_properties.driverVersion; + memcpy(header->uuid, physical_device_properties.pipelineCacheUUID, VK_UUID_SIZE); + header->driver_abi = sizeof(void *); + + pipeline_cache_id = String::hex_encode_buffer(physical_device_properties.pipelineCacheUUID, VK_UUID_SIZE); + pipeline_cache_id += "-driver-" + itos(physical_device_properties.driverVersion); + + return OK; +} + +static void _convert_subpass_attachments(const VkAttachmentReference2 *p_attachment_references_2, uint32_t p_attachment_references_count, TightLocalVector &r_attachment_references) { + r_attachment_references.resize(p_attachment_references_count); + for (uint32_t i = 0; i < p_attachment_references_count; i++) { + // Ignore sType, pNext and aspectMask (which is currently unused). + r_attachment_references[i].attachment = p_attachment_references_2[i].attachment; + r_attachment_references[i].layout = p_attachment_references_2[i].layout; + } +} + +VkResult RenderingDeviceDriverVulkan::_create_render_pass(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { + if (device_functions.CreateRenderPass2KHR != nullptr) { + return device_functions.CreateRenderPass2KHR(p_device, p_create_info, p_allocator, p_render_pass); + } else { + // Compatibility fallback with regular create render pass but by converting the inputs from the newer version to the older one. + TightLocalVector attachments; + attachments.resize(p_create_info->attachmentCount); + for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { + // Ignores sType and pNext from the attachment. + const VkAttachmentDescription2 &src = p_create_info->pAttachments[i]; + VkAttachmentDescription &dst = attachments[i]; + dst.flags = src.flags; + dst.format = src.format; + dst.samples = src.samples; + dst.loadOp = src.loadOp; + dst.storeOp = src.storeOp; + dst.stencilLoadOp = src.stencilLoadOp; + dst.stencilStoreOp = src.stencilStoreOp; + dst.initialLayout = src.initialLayout; + dst.finalLayout = src.finalLayout; + } + + const uint32_t attachment_vectors_per_subpass = 4; + TightLocalVector> subpasses_attachments; + TightLocalVector subpasses; + subpasses_attachments.resize(p_create_info->subpassCount * attachment_vectors_per_subpass); + subpasses.resize(p_create_info->subpassCount); + + for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { + const uint32_t vector_base_index = i * attachment_vectors_per_subpass; + const uint32_t input_attachments_index = vector_base_index + 0; + const uint32_t color_attachments_index = vector_base_index + 1; + const uint32_t resolve_attachments_index = vector_base_index + 2; + const uint32_t depth_attachment_index = vector_base_index + 3; + _convert_subpass_attachments(p_create_info->pSubpasses[i].pInputAttachments, p_create_info->pSubpasses[i].inputAttachmentCount, subpasses_attachments[input_attachments_index]); + _convert_subpass_attachments(p_create_info->pSubpasses[i].pColorAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[color_attachments_index]); + _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[resolve_attachments_index]); + _convert_subpass_attachments(p_create_info->pSubpasses[i].pDepthStencilAttachment, (p_create_info->pSubpasses[i].pDepthStencilAttachment != nullptr) ? 1 : 0, subpasses_attachments[depth_attachment_index]); + + // Ignores sType and pNext from the subpass. + const VkSubpassDescription2 &src_subpass = p_create_info->pSubpasses[i]; + VkSubpassDescription &dst_subpass = subpasses[i]; + dst_subpass.flags = src_subpass.flags; + dst_subpass.pipelineBindPoint = src_subpass.pipelineBindPoint; + dst_subpass.inputAttachmentCount = src_subpass.inputAttachmentCount; + dst_subpass.pInputAttachments = subpasses_attachments[input_attachments_index].ptr(); + dst_subpass.colorAttachmentCount = src_subpass.colorAttachmentCount; + dst_subpass.pColorAttachments = subpasses_attachments[color_attachments_index].ptr(); + dst_subpass.pResolveAttachments = subpasses_attachments[resolve_attachments_index].ptr(); + dst_subpass.pDepthStencilAttachment = subpasses_attachments[depth_attachment_index].ptr(); + dst_subpass.preserveAttachmentCount = src_subpass.preserveAttachmentCount; + dst_subpass.pPreserveAttachments = src_subpass.pPreserveAttachments; + } + + TightLocalVector dependencies; + dependencies.resize(p_create_info->dependencyCount); + + for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { + // Ignores sType and pNext from the dependency, and viewMask which is currently unused. + const VkSubpassDependency2 &src_dependency = p_create_info->pDependencies[i]; + VkSubpassDependency &dst_dependency = dependencies[i]; + dst_dependency.srcSubpass = src_dependency.srcSubpass; + dst_dependency.dstSubpass = src_dependency.dstSubpass; + dst_dependency.srcStageMask = src_dependency.srcStageMask; + dst_dependency.dstStageMask = src_dependency.dstStageMask; + dst_dependency.srcAccessMask = src_dependency.srcAccessMask; + dst_dependency.dstAccessMask = src_dependency.dstAccessMask; + dst_dependency.dependencyFlags = src_dependency.dependencyFlags; + } + + VkRenderPassCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + create_info.pNext = p_create_info->pNext; + create_info.flags = p_create_info->flags; + create_info.attachmentCount = attachments.size(); + create_info.pAttachments = attachments.ptr(); + create_info.subpassCount = subpasses.size(); + create_info.pSubpasses = subpasses.ptr(); + create_info.dependencyCount = dependencies.size(); + create_info.pDependencies = dependencies.ptr(); + return vkCreateRenderPass(vk_device, &create_info, p_allocator, p_render_pass); + } +} + +bool RenderingDeviceDriverVulkan::_release_image_semaphore(CommandQueue *p_command_queue, uint32_t p_semaphore_index, bool p_release_on_swap_chain) { + SwapChain *swap_chain = p_command_queue->image_semaphores_swap_chains[p_semaphore_index]; + if (swap_chain != nullptr) { + // Clear the swap chain from the command queue's vector. + p_command_queue->image_semaphores_swap_chains[p_semaphore_index] = nullptr; + + if (p_release_on_swap_chain) { + // Remove the acquired semaphore from the swap chain's vectors. + for (uint32_t i = 0; i < swap_chain->command_queues_acquired.size(); i++) { + if (swap_chain->command_queues_acquired[i] == p_command_queue && swap_chain->command_queues_acquired_semaphores[i] == p_semaphore_index) { + swap_chain->command_queues_acquired.remove_at(i); + swap_chain->command_queues_acquired_semaphores.remove_at(i); + break; + } + } + } + + return true; + } + + return false; +} + +bool RenderingDeviceDriverVulkan::_recreate_image_semaphore(CommandQueue *p_command_queue, uint32_t p_semaphore_index, bool p_release_on_swap_chain) { + _release_image_semaphore(p_command_queue, p_semaphore_index, p_release_on_swap_chain); + + VkSemaphore semaphore; + VkSemaphoreCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkResult err = vkCreateSemaphore(vk_device, &create_info, nullptr, &semaphore); + ERR_FAIL_COND_V(err != VK_SUCCESS, false); + + // Indicate the semaphore is free again and destroy the previous one before storing the new one. + vkDestroySemaphore(vk_device, p_command_queue->image_semaphores[p_semaphore_index], nullptr); + + p_command_queue->image_semaphores[p_semaphore_index] = semaphore; + p_command_queue->free_image_semaphores.push_back(p_semaphore_index); + + return true; +} + +void RenderingDeviceDriverVulkan::_set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + if (functions.SetDebugUtilsObjectNameEXT != nullptr) { + CharString obj_data = p_object_name.utf8(); + VkDebugUtilsObjectNameInfoEXT name_info; + name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + name_info.pNext = nullptr; + name_info.objectType = p_object_type; + name_info.objectHandle = p_object_handle; + name_info.pObjectName = obj_data.get_data(); + functions.SetDebugUtilsObjectNameEXT(vk_device, &name_info); + } +} + +Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t p_frame_count) { + // Frame count is not required for the Vulkan driver, so we just ignore it. + + context_device = context_driver->device_get(p_device_index); + physical_device = context_driver->physical_device_get(p_device_index); + vkGetPhysicalDeviceProperties(physical_device, &physical_device_properties); + + // Copy the queue family properties the context already retrieved. + uint32_t queue_family_count = context_driver->queue_family_get_count(p_device_index); + queue_family_properties.resize(queue_family_count); + for (uint32_t i = 0; i < queue_family_count; i++) { + queue_family_properties[i] = context_driver->queue_family_get(p_device_index, i); + } + + Error err = _initialize_device_extensions(); + ERR_FAIL_COND_V(err != OK, err); + + err = _check_device_features(); + ERR_FAIL_COND_V(err != OK, err); + + err = _check_device_capabilities(); + ERR_FAIL_COND_V(err != OK, err); + + LocalVector queue_create_info; + err = _add_queue_create_info(queue_create_info); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_device(queue_create_info); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_allocator(); + ERR_FAIL_COND_V(err != OK, err); + + err = _initialize_pipeline_cache(); + ERR_FAIL_COND_V(err != OK, err); + + max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool"); + + return OK; +} + /****************/ /**** MEMORY ****/ /****************/ @@ -472,7 +1351,7 @@ static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_ASPECT_DEPTH_BIT, VK_IMAGE_ASPECT_ static_assert(ENUM_MEMBERS_EQUAL(RDD::TEXTURE_ASPECT_STENCIL_BIT, VK_IMAGE_ASPECT_STENCIL_BIT)); VkSampleCountFlagBits RenderingDeviceDriverVulkan::_ensure_supported_sample_count(TextureSamples p_requested_sample_count) { - VkSampleCountFlags sample_count_flags = (context->get_device_limits().framebufferColorSampleCounts & limits.framebufferDepthSampleCounts); + VkSampleCountFlags sample_count_flags = (physical_device_properties.limits.framebufferColorSampleCounts & physical_device_properties.limits.framebufferDepthSampleCounts); if ((sample_count_flags & RD_TO_VK_SAMPLE_COUNT[p_requested_sample_count])) { // The requested sample count is supported. @@ -497,7 +1376,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat & if (p_format.shareable_formats.size()) { create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - if (context->is_device_extension_enabled(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME)) { + if (enabled_device_extension_names.has(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME)) { VkFormat *vk_allowed_formats = ALLOCA_ARRAY(VkFormat, p_format.shareable_formats.size()); for (int i = 0; i < p_format.shareable_formats.size(); i++) { vk_allowed_formats[i] = RD_TO_VK_FORMAT[p_format.shareable_formats[i]]; @@ -678,7 +1557,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared(TextureID p_or image_view_create_info.components.b = (VkComponentSwizzle)p_view.swizzle_b; image_view_create_info.components.a = (VkComponentSwizzle)p_view.swizzle_a; - if (context->is_device_extension_enabled(VK_KHR_MAINTENANCE_2_EXTENSION_NAME)) { + if (enabled_device_extension_names.has(VK_KHR_MAINTENANCE_2_EXTENSION_NAME)) { // May need to make VK_KHR_maintenance2 mandatory and thus has Vulkan 1.1 be our minimum supported version // if we require setting this information. Vulkan 1.0 may simply not care. if (image_view_create_info.format != owner_tex_info->vk_view_create_info.format) { @@ -690,7 +1569,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared(TextureID p_or // Certain features may not be available for the format of the view. { VkFormatProperties properties = {}; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), RD_TO_VK_FORMAT[p_view.format], &properties); + vkGetPhysicalDeviceFormatProperties(physical_device, RD_TO_VK_FORMAT[p_view.format], &properties); const VkFormatFeatureFlags &supported_flags = owner_tex_info->vk_create_info.tiling == VK_IMAGE_TILING_LINEAR ? properties.linearTilingFeatures : properties.optimalTilingFeatures; if ((usage_info->usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(supported_flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) { usage_info->usage &= ~VK_IMAGE_USAGE_STORAGE_BIT; @@ -864,7 +1743,7 @@ void RenderingDeviceDriverVulkan::texture_unmap(TextureID p_texture) { BitField RenderingDeviceDriverVulkan::texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) { VkFormatProperties properties = {}; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), RD_TO_VK_FORMAT[p_format], &properties); + vkGetPhysicalDeviceFormatProperties(physical_device, RD_TO_VK_FORMAT[p_format], &properties); const VkFormatFeatureFlags &flags = p_cpu_readable ? properties.linearTilingFeatures : properties.optimalTilingFeatures; @@ -925,7 +1804,7 @@ RDD::SamplerID RenderingDeviceDriverVulkan::sampler_create(const SamplerState &p sampler_create_info.addressModeV = (VkSamplerAddressMode)p_state.repeat_v; sampler_create_info.addressModeW = (VkSamplerAddressMode)p_state.repeat_w; sampler_create_info.mipLodBias = p_state.lod_bias; - sampler_create_info.anisotropyEnable = p_state.use_anisotropy && context->get_physical_device_features().samplerAnisotropy; + sampler_create_info.anisotropyEnable = p_state.use_anisotropy && (physical_device_features.samplerAnisotropy == VK_TRUE); sampler_create_info.maxAnisotropy = p_state.anisotropy_max; sampler_create_info.compareEnable = p_state.enable_compare; sampler_create_info.compareOp = (VkCompareOp)p_state.compare_op; @@ -947,12 +1826,12 @@ void RenderingDeviceDriverVulkan::sampler_free(SamplerID p_sampler) { bool RenderingDeviceDriverVulkan::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) { switch (p_filter) { - case RD::SAMPLER_FILTER_NEAREST: { + case SAMPLER_FILTER_NEAREST: { return true; } - case RD::SAMPLER_FILTER_LINEAR: { + case SAMPLER_FILTER_LINEAR: { VkFormatProperties properties = {}; - vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), RD_TO_VK_FORMAT[p_format], &properties); + vkGetPhysicalDeviceFormatProperties(physical_device, RD_TO_VK_FORMAT[p_format], &properties); return (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); } } @@ -1112,76 +1991,353 @@ void RenderingDeviceDriverVulkan::command_pipeline_barrier( p_texture_barriers.size(), vk_image_barriers); } -/*************************/ -/**** COMMAND BUFFERS ****/ -/*************************/ +/****************/ +/**** FENCES ****/ +/****************/ + +RDD::FenceID RenderingDeviceDriverVulkan::fence_create() { + VkFence vk_fence = VK_NULL_HANDLE; + VkFenceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkResult err = vkCreateFence(vk_device, &create_info, nullptr, &vk_fence); + ERR_FAIL_COND_V(err != VK_SUCCESS, FenceID()); + + Fence *fence = memnew(Fence); + fence->vk_fence = vk_fence; + fence->queue_signaled_from = nullptr; + return FenceID(fence); +} + +Error RenderingDeviceDriverVulkan::fence_wait(FenceID p_fence) { + Fence *fence = (Fence *)(p_fence.id); + VkResult err = vkWaitForFences(vk_device, 1, &fence->vk_fence, VK_TRUE, UINT64_MAX); + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + + err = vkResetFences(vk_device, 1, &fence->vk_fence); + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + + if (fence->queue_signaled_from != nullptr) { + // Release all semaphores that the command queue associated to the fence waited on the last time it was submitted. + LocalVector> &pairs = fence->queue_signaled_from->image_semaphores_for_fences; + uint32_t i = 0; + while (i < pairs.size()) { + if (pairs[i].first == fence) { + _release_image_semaphore(fence->queue_signaled_from, pairs[i].second, true); + fence->queue_signaled_from->free_image_semaphores.push_back(pairs[i].second); + pairs.remove_at(i); + } else { + i++; + } + } + + fence->queue_signaled_from = nullptr; + } + + return OK; +} + +void RenderingDeviceDriverVulkan::fence_free(FenceID p_fence) { + Fence *fence = (Fence *)(p_fence.id); + vkDestroyFence(vk_device, fence->vk_fence, nullptr); + memdelete(fence); +} + +/********************/ +/**** SEMAPHORES ****/ +/********************/ + +RDD::SemaphoreID RenderingDeviceDriverVulkan::semaphore_create() { + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkResult err = vkCreateSemaphore(vk_device, &create_info, nullptr, &semaphore); + ERR_FAIL_COND_V(err != VK_SUCCESS, SemaphoreID()); + + return SemaphoreID(semaphore); +} + +void RenderingDeviceDriverVulkan::semaphore_free(SemaphoreID p_semaphore) { + vkDestroySemaphore(vk_device, VkSemaphore(p_semaphore.id), nullptr); +} + +/******************/ +/**** COMMANDS ****/ +/******************/ + +// ----- QUEUE FAMILY ----- + +RDD::CommandQueueFamilyID RenderingDeviceDriverVulkan::command_queue_family_get(BitField p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface) { + // Pick the queue with the least amount of bits that can fulfill the requirements. + VkQueueFlags picked_queue_flags = VK_QUEUE_FLAG_BITS_MAX_ENUM; + uint32_t picked_family_index = UINT_MAX; + for (uint32_t i = 0; i < queue_family_properties.size(); i++) { + if (queue_families[i].is_empty()) { + // Ignore empty queue families. + continue; + } + + if (p_surface != 0 && !context_driver->queue_family_supports_present(physical_device, i, p_surface)) { + // Present is not an actual bit but something that must be queried manually. + continue; + } + + // Preferring a queue with less bits will get us closer to getting a queue that performs better for our requirements. + // For example, dedicated compute and transfer queues are usually indicated as such. + const VkQueueFlags option_queue_flags = queue_family_properties[i].queueFlags; + const bool includes_all_bits = (option_queue_flags & p_cmd_queue_family_bits) == p_cmd_queue_family_bits; + const bool prefer_less_bits = option_queue_flags < picked_queue_flags; + if (includes_all_bits && prefer_less_bits) { + picked_family_index = i; + picked_queue_flags = option_queue_flags; + } + } + + ERR_FAIL_COND_V_MSG(picked_family_index >= queue_family_properties.size(), CommandQueueFamilyID(), "A queue family with the requested bits could not be found."); + + // Since 0 is a valid index and we use 0 as the error case, we make the index start from 1 instead. + return CommandQueueFamilyID(picked_family_index + 1); +} + +// ----- QUEUE ----- + +RDD::CommandQueueID RenderingDeviceDriverVulkan::command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue) { + DEV_ASSERT(p_cmd_queue_family.id != 0); + + // Make a virtual queue on top of a real queue. Use the queue from the family with the least amount of virtual queues created. + uint32_t family_index = p_cmd_queue_family.id - 1; + TightLocalVector &queue_family = queue_families[family_index]; + uint32_t picked_queue_index = UINT_MAX; + uint32_t picked_virtual_count = UINT_MAX; + for (uint32_t i = 0; i < queue_family.size(); i++) { + if (queue_family[i].virtual_count < picked_virtual_count) { + picked_queue_index = i; + picked_virtual_count = queue_family[i].virtual_count; + } + } + + ERR_FAIL_COND_V_MSG(picked_queue_index >= queue_family.size(), CommandQueueID(), "A queue in the picked family could not be found."); + + // Create the virtual queue. + CommandQueue *command_queue = memnew(CommandQueue); + command_queue->queue_family = family_index; + command_queue->queue_index = picked_queue_index; + queue_family[picked_queue_index].virtual_count++; + + // If is was identified as the main queue and a hook is active, indicate it as such to the hook. + if (p_identify_as_main_queue && (VulkanHooks::get_singleton() != nullptr)) { + VulkanHooks::get_singleton()->set_direct_queue_family_and_index(family_index, picked_queue_index); + } + + return CommandQueueID(command_queue); +} + +Error RenderingDeviceDriverVulkan::command_queue_execute(CommandQueueID p_cmd_queue, VectorView p_cmd_buffers, VectorView p_wait_semaphores, VectorView p_signal_semaphores, FenceID p_signal_fence) { + DEV_ASSERT(p_cmd_queue.id != 0); + + CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + Queue &device_queue = queue_families[command_queue->queue_family][command_queue->queue_index]; + Fence *fence = (Fence *)(p_signal_fence.id); + VkFence vk_fence = (fence != nullptr) ? fence->vk_fence : VK_NULL_HANDLE; + + thread_local LocalVector command_buffers; + thread_local LocalVector wait_semaphores; + thread_local LocalVector signal_semaphores; + thread_local LocalVector wait_semaphores_stages; + command_buffers.clear(); + wait_semaphores.clear(); + signal_semaphores.clear(); + wait_semaphores_stages.clear(); + + if (!command_queue->pending_semaphores_for_execute.is_empty()) { + for (uint32_t i = 0; i < command_queue->pending_semaphores_for_execute.size(); i++) { + VkSemaphore wait_semaphore = command_queue->image_semaphores[command_queue->pending_semaphores_for_execute[i]]; + wait_semaphores.push_back(wait_semaphore); + wait_semaphores_stages.push_back(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + } + + command_queue->pending_semaphores_for_execute.clear(); + } + + for (uint32_t i = 0; i < p_cmd_buffers.size(); i++) { + command_buffers.push_back(VkCommandBuffer(p_cmd_buffers[i].id)); + } + + for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { + // FIXME: Allow specifying the stage mask in more detail. + wait_semaphores.push_back(VkSemaphore(p_wait_semaphores[i].id)); + wait_semaphores_stages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + } + + for (uint32_t i = 0; i < p_signal_semaphores.size(); i++) { + signal_semaphores.push_back(VkSemaphore(p_signal_semaphores[i].id)); + } + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = wait_semaphores.size(); + submit_info.pWaitSemaphores = wait_semaphores.ptr(); + submit_info.pWaitDstStageMask = wait_semaphores_stages.ptr(); + submit_info.commandBufferCount = command_buffers.size(); + submit_info.pCommandBuffers = command_buffers.ptr(); + submit_info.signalSemaphoreCount = signal_semaphores.size(); + submit_info.pSignalSemaphores = signal_semaphores.ptr(); + + device_queue.submit_mutex.lock(); + VkResult err = vkQueueSubmit(device_queue.queue, 1, &submit_info, vk_fence); + device_queue.submit_mutex.unlock(); + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + + if (fence != nullptr && !command_queue->pending_semaphores_for_fence.is_empty()) { + fence->queue_signaled_from = command_queue; + + // Indicate to the fence that it should release the semaphores that were waited on this submission the next time the fence is waited on. + for (uint32_t i = 0; i < command_queue->pending_semaphores_for_fence.size(); i++) { + command_queue->image_semaphores_for_fences.push_back({ fence, command_queue->pending_semaphores_for_fence[i] }); + } + + command_queue->pending_semaphores_for_fence.clear(); + } + + return OK; +} + +Error RenderingDeviceDriverVulkan::command_queue_present(CommandQueueID p_cmd_queue, VectorView p_swap_chains, VectorView p_wait_semaphores) { + DEV_ASSERT(p_cmd_queue.id != 0); + + CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + Queue &device_queue = queue_families[command_queue->queue_family][command_queue->queue_index]; + + thread_local LocalVector swapchains; + thread_local LocalVector image_indices; + thread_local LocalVector wait_semaphores; + thread_local LocalVector results; + swapchains.clear(); + image_indices.clear(); + for (uint32_t i = 0; i < p_swap_chains.size(); i++) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); + swapchains.push_back(swap_chain->vk_swapchain); + DEV_ASSERT(swap_chain->image_index < swap_chain->images.size()); + image_indices.push_back(swap_chain->image_index); + } + + wait_semaphores.clear(); + for (uint32_t i = 0; i < p_wait_semaphores.size(); i++) { + wait_semaphores.push_back(VkSemaphore(p_wait_semaphores[i].id)); + } + + results.resize(swapchains.size()); + + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = wait_semaphores.size(); + present_info.pWaitSemaphores = wait_semaphores.ptr(); + present_info.swapchainCount = swapchains.size(); + present_info.pSwapchains = swapchains.ptr(); + present_info.pImageIndices = image_indices.ptr(); + present_info.pResults = results.ptr(); + device_queue.submit_mutex.lock(); + VkResult err = device_functions.QueuePresentKHR(device_queue.queue, &present_info); + device_queue.submit_mutex.unlock(); + + // Set the index to an invalid value. If any of the swap chains returned out of date, indicate it should be resized the next time it's acquired. + bool any_result_is_out_of_date = false; + for (uint32_t i = 0; i < p_swap_chains.size(); i++) { + SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id); + swap_chain->image_index = UINT_MAX; + if (results[i] == VK_ERROR_OUT_OF_DATE_KHR || results[i] == VK_SUBOPTIMAL_KHR) { + context_driver->surface_set_needs_resize(swap_chain->surface, true); + any_result_is_out_of_date = true; + } + } + + if (any_result_is_out_of_date || err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { + // It is possible for presentation to fail with out of date while acquire might've succeeded previously. This case + // will be considered a silent failure as it can be triggered easily by resizing a window in the OS natively. + return FAILED; + } + + ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED); + + return OK; +} + +void RenderingDeviceDriverVulkan::command_queue_free(CommandQueueID p_cmd_queue) { + DEV_ASSERT(p_cmd_queue); + + CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + + // Erase all the semaphores used for image acquisition. + for (VkSemaphore semaphore : command_queue->image_semaphores) { + vkDestroySemaphore(vk_device, semaphore, nullptr); + } + + // Retrieve the queue family corresponding to the virtual queue. + DEV_ASSERT(command_queue->queue_family < queue_families.size()); + TightLocalVector &queue_family = queue_families[command_queue->queue_family]; + + // Decrease the virtual queue count. + DEV_ASSERT(command_queue->queue_index < queue_family.size()); + DEV_ASSERT(queue_family[command_queue->queue_index].virtual_count > 0); + queue_family[command_queue->queue_index].virtual_count--; + + // Destroy the virtual queue structure. + memdelete(command_queue); +} // ----- POOL ----- -RDD::CommandPoolID RenderingDeviceDriverVulkan::command_pool_create(CommandBufferType p_cmd_buffer_type) { +RDD::CommandPoolID RenderingDeviceDriverVulkan::command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) { + DEV_ASSERT(p_cmd_queue_family.id != 0); + + uint32_t family_index = p_cmd_queue_family.id - 1; VkCommandPoolCreateInfo cmd_pool_info = {}; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.queueFamilyIndex = context->get_graphics_queue_family_index(); + cmd_pool_info.queueFamilyIndex = family_index; cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkCommandPool vk_cmd_pool = VK_NULL_HANDLE; - VkResult res = vkCreateCommandPool(vk_device, &cmd_pool_info, nullptr, &vk_cmd_pool); + VkCommandPool vk_command_pool = VK_NULL_HANDLE; + VkResult res = vkCreateCommandPool(vk_device, &cmd_pool_info, nullptr, &vk_command_pool); ERR_FAIL_COND_V_MSG(res, CommandPoolID(), "vkCreateCommandPool failed with error " + itos(res) + "."); -#ifdef DEBUG_ENABLED - if (p_cmd_buffer_type == COMMAND_BUFFER_TYPE_SECONDARY) { - secondary_cmd_pools.insert(CommandPoolID(vk_cmd_pool)); - } -#endif - - return CommandPoolID(vk_cmd_pool); + CommandPool *command_pool = memnew(CommandPool); + command_pool->vk_command_pool = vk_command_pool; + command_pool->buffer_type = p_cmd_buffer_type; + return CommandPoolID(command_pool); } void RenderingDeviceDriverVulkan::command_pool_free(CommandPoolID p_cmd_pool) { - vkDestroyCommandPool(vk_device, (VkCommandPool)p_cmd_pool.id, nullptr); + DEV_ASSERT(p_cmd_pool); -#ifdef DEBUG_ENABLED - secondary_cmd_pools.erase(p_cmd_pool); -#endif + CommandPool *command_pool = (CommandPool *)(p_cmd_pool.id); + vkDestroyCommandPool(vk_device, command_pool->vk_command_pool, nullptr); + memdelete(command_pool); } // ----- BUFFER ----- -RDD::CommandBufferID RenderingDeviceDriverVulkan::command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) { -#ifdef DEBUG_ENABLED - if (p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY) { - ERR_FAIL_COND_V(secondary_cmd_pools.has(p_cmd_pool), CommandBufferID()); - } else { - ERR_FAIL_COND_V(!secondary_cmd_pools.has(p_cmd_pool), CommandBufferID()); - } -#endif +RDD::CommandBufferID RenderingDeviceDriverVulkan::command_buffer_create(CommandPoolID p_cmd_pool) { + DEV_ASSERT(p_cmd_pool); + const CommandPool *command_pool = (const CommandPool *)(p_cmd_pool.id); VkCommandBufferAllocateInfo cmd_buf_info = {}; cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmd_buf_info.commandPool = (VkCommandPool)p_cmd_pool.id; - cmd_buf_info.level = p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY ? VK_COMMAND_BUFFER_LEVEL_PRIMARY : VK_COMMAND_BUFFER_LEVEL_SECONDARY; + cmd_buf_info.commandPool = command_pool->vk_command_pool; cmd_buf_info.commandBufferCount = 1; + if (command_pool->buffer_type == COMMAND_BUFFER_TYPE_SECONDARY) { + cmd_buf_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; + } else { + cmd_buf_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + } + VkCommandBuffer vk_cmd_buffer = VK_NULL_HANDLE; VkResult err = vkAllocateCommandBuffers(vk_device, &cmd_buf_info, &vk_cmd_buffer); ERR_FAIL_COND_V_MSG(err, CommandBufferID(), "vkAllocateCommandBuffers failed with error " + itos(err) + "."); - CommandBufferID cmd_buffer_id = CommandBufferID(vk_cmd_buffer); -#ifdef DEBUG_ENABLED - // Erase first because Vulkan may reuse a handle. - secondary_cmd_buffers.erase(cmd_buffer_id); - if (p_cmd_buffer_type == COMMAND_BUFFER_TYPE_SECONDARY) { - secondary_cmd_buffers.insert(cmd_buffer_id); - } -#endif - return cmd_buffer_id; + return CommandBufferID(vk_cmd_buffer); } bool RenderingDeviceDriverVulkan::command_buffer_begin(CommandBufferID p_cmd_buffer) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(secondary_cmd_buffers.has(p_cmd_buffer), false); -#endif - // Reset is implicit (VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT). VkCommandBufferBeginInfo cmd_buf_begin_info = {}; @@ -1195,10 +2351,6 @@ bool RenderingDeviceDriverVulkan::command_buffer_begin(CommandBufferID p_cmd_buf } bool RenderingDeviceDriverVulkan::command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(!secondary_cmd_buffers.has(p_cmd_buffer), false); -#endif - // Reset is implicit (VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT). VkCommandBufferInheritanceInfo inheritance_info = {}; @@ -1223,16 +2375,408 @@ void RenderingDeviceDriverVulkan::command_buffer_end(CommandBufferID p_cmd_buffe } void RenderingDeviceDriverVulkan::command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView p_secondary_cmd_buffers) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(secondary_cmd_buffers.has(p_cmd_buffer)); - for (uint32_t i = 0; i < p_secondary_cmd_buffers.size(); i++) { - ERR_FAIL_COND(!secondary_cmd_buffers.has(p_secondary_cmd_buffers[i])); - } -#endif - vkCmdExecuteCommands((VkCommandBuffer)p_cmd_buffer.id, p_secondary_cmd_buffers.size(), (const VkCommandBuffer *)p_secondary_cmd_buffers.ptr()); } +/********************/ +/**** SWAP CHAIN ****/ +/********************/ + +void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) { + // Destroy views and framebuffers associated to the swapchain's images. + for (FramebufferID framebuffer : swap_chain->framebuffers) { + framebuffer_free(framebuffer); + } + + for (VkImageView view : swap_chain->image_views) { + vkDestroyImageView(vk_device, view, nullptr); + } + + swap_chain->image_index = UINT_MAX; + swap_chain->images.clear(); + swap_chain->image_views.clear(); + swap_chain->framebuffers.clear(); + + if (swap_chain->vk_swapchain != VK_NULL_HANDLE) { + device_functions.DestroySwapchainKHR(vk_device, swap_chain->vk_swapchain, nullptr); + swap_chain->vk_swapchain = VK_NULL_HANDLE; + } + + for (uint32_t i = 0; i < swap_chain->command_queues_acquired.size(); i++) { + _recreate_image_semaphore(swap_chain->command_queues_acquired[i], swap_chain->command_queues_acquired_semaphores[i], false); + } + + swap_chain->command_queues_acquired.clear(); + swap_chain->command_queues_acquired_semaphores.clear(); +} + +RenderingDeviceDriver::SwapChainID RenderingDeviceDriverVulkan::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) { + DEV_ASSERT(p_surface != 0); + + RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface); + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + + // Retrieve the formats supported by the surface. + uint32_t format_count = 0; + VkResult err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID()); + + TightLocalVector formats; + formats.resize(format_count); + err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, formats.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID()); + + VkFormat format = VK_FORMAT_UNDEFINED; + VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) { + // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the surface has no preferred format. + format = VK_FORMAT_B8G8R8A8_UNORM; + color_space = formats[0].colorSpace; + } else if (format_count > 0) { + // Use one of the supported formats, prefer B8G8R8A8_UNORM. + const VkFormat preferred_format = VK_FORMAT_B8G8R8A8_UNORM; + const VkFormat second_format = VK_FORMAT_R8G8B8A8_UNORM; + for (uint32_t i = 0; i < format_count; i++) { + if (formats[i].format == preferred_format || formats[i].format == second_format) { + format = formats[i].format; + if (formats[i].format == preferred_format) { + // This is the preferred format, stop searching. + break; + } + } + } + } + + // No formats are supported. + ERR_FAIL_COND_V_MSG(format == VK_FORMAT_UNDEFINED, SwapChainID(), "Surface did not return any valid formats."); + + // Create the render pass for the chosen format. + VkAttachmentDescription2KHR attachment = {}; + attachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; + attachment.format = format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference2KHR color_reference = {}; + color_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription2KHR subpass = {}; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + + VkRenderPassCreateInfo2KHR pass_info = {}; + pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; + pass_info.attachmentCount = 1; + pass_info.pAttachments = &attachment; + pass_info.subpassCount = 1; + pass_info.pSubpasses = &subpass; + + VkRenderPass render_pass = VK_NULL_HANDLE; + err = _create_render_pass(vk_device, &pass_info, nullptr, &render_pass); + ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID()); + + SwapChain *swap_chain = memnew(SwapChain); + swap_chain->surface = p_surface; + swap_chain->format = format; + swap_chain->color_space = color_space; + swap_chain->render_pass = RenderPassID(render_pass); + return SwapChainID(swap_chain); +} + +Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) { + DEV_ASSERT(p_cmd_queue.id != 0); + DEV_ASSERT(p_swap_chain.id != 0); + + CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + + // Release all current contents of the swap chain. + _swap_chain_release(swap_chain); + + // Validate if the command queue being used supports creating the swap chain for this surface. + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + if (!context_driver->queue_family_supports_present(physical_device, command_queue->queue_family, swap_chain->surface)) { + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Surface is not supported by device. Did the GPU go offline? Was the window created on another monitor? Check" + "previous errors & try launching with --gpu-validation."); + } + + // Retrieve the surface's capabilities. + RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(swap_chain->surface); + VkSurfaceCapabilitiesKHR surface_capabilities = {}; + VkResult err = functions.GetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface->vk_surface, &surface_capabilities); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + VkExtent2D extent; + if (surface_capabilities.currentExtent.width == 0xFFFFFFFF) { + // The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities. + extent.width = CLAMP(surface->width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); + extent.height = CLAMP(surface->height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height); + } else { + // Grab the dimensions from the current extent. + extent = surface_capabilities.currentExtent; + surface->width = extent.width; + surface->height = extent.height; + } + + if (surface->width == 0 || surface->height == 0) { + // The surface doesn't have valid dimensions, so we can't create a swap chain. + return ERR_SKIP; + } + + // Find what present modes are supported. + TightLocalVector present_modes; + uint32_t present_modes_count = 0; + err = functions.GetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface->vk_surface, &present_modes_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + present_modes.resize(present_modes_count); + err = functions.GetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface->vk_surface, &present_modes_count, present_modes.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + // Choose the present mode based on the display server setting. + VkPresentModeKHR present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + String present_mode_name = "Enabled"; + switch (surface->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + present_mode = VK_PRESENT_MODE_MAILBOX_KHR; + present_mode_name = "Mailbox"; + break; + case DisplayServer::VSYNC_ADAPTIVE: + present_mode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; + present_mode_name = "Adaptive"; + break; + case DisplayServer::VSYNC_ENABLED: + present_mode = VK_PRESENT_MODE_FIFO_KHR; + present_mode_name = "Enabled"; + break; + case DisplayServer::VSYNC_DISABLED: + present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + present_mode_name = "Disabled"; + break; + } + + bool present_mode_available = present_modes.find(present_mode) >= 0; + if (present_mode_available) { + print_verbose("Using present mode: " + present_mode_name); + } else { + // Present mode is not available, fall back to FIFO which is guaranteed to be supported. + WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_name)); + surface->vsync_mode = DisplayServer::VSYNC_ENABLED; + present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; + } + + // Clamp the desired image count to the surface's capabilities. + uint32_t desired_swapchain_images = MAX(p_desired_framebuffer_count, surface_capabilities.minImageCount); + if (surface_capabilities.maxImageCount > 0) { + // Only clamp to the max image count if it's defined. A max image count of 0 means there's no upper limit to the amount of images. + desired_swapchain_images = MIN(desired_swapchain_images, surface_capabilities.maxImageCount); + } + + // Prefer identity transform if it's supported, use the current transform otherwise. + VkSurfaceTransformFlagBitsKHR surface_transform_bits; + if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { + surface_transform_bits = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } else { + surface_transform_bits = surface_capabilities.currentTransform; + } + + VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + if (OS::get_singleton()->is_layered_allowed() || !(surface_capabilities.supportedCompositeAlpha & composite_alpha)) { + // Find a supported composite alpha mode - one of these is guaranteed to be set. + VkCompositeAlphaFlagBitsKHR composite_alpha_flags[4] = { + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR + }; + + for (uint32_t i = 0; i < ARRAY_SIZE(composite_alpha_flags); i++) { + if (surface_capabilities.supportedCompositeAlpha & composite_alpha_flags[i]) { + composite_alpha = composite_alpha_flags[i]; + break; + } + } + } + + VkSwapchainCreateInfoKHR swap_create_info = {}; + swap_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swap_create_info.surface = surface->vk_surface; + swap_create_info.minImageCount = desired_swapchain_images; + swap_create_info.imageFormat = swap_chain->format; + swap_create_info.imageColorSpace = swap_chain->color_space; + swap_create_info.imageExtent = extent; + swap_create_info.imageArrayLayers = 1; + swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swap_create_info.preTransform = surface_transform_bits; + swap_create_info.compositeAlpha = composite_alpha; + swap_create_info.presentMode = present_mode; + swap_create_info.clipped = true; + err = device_functions.CreateSwapchainKHR(vk_device, &swap_create_info, nullptr, &swap_chain->vk_swapchain); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + uint32_t image_count = 0; + err = device_functions.GetSwapchainImagesKHR(vk_device, swap_chain->vk_swapchain, &image_count, nullptr); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + swap_chain->images.resize(image_count); + err = device_functions.GetSwapchainImagesKHR(vk_device, swap_chain->vk_swapchain, &image_count, swap_chain->images.ptr()); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + VkImageViewCreateInfo view_create_info = {}; + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = swap_chain->format; + view_create_info.components.r = VK_COMPONENT_SWIZZLE_R; + view_create_info.components.g = VK_COMPONENT_SWIZZLE_G; + view_create_info.components.b = VK_COMPONENT_SWIZZLE_B; + view_create_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.layerCount = 1; + + swap_chain->image_views.reserve(image_count); + + VkImageView image_view; + for (uint32_t i = 0; i < image_count; i++) { + view_create_info.image = swap_chain->images[i]; + err = vkCreateImageView(vk_device, &view_create_info, nullptr, &image_view); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + swap_chain->image_views.push_back(image_view); + } + + swap_chain->framebuffers.reserve(image_count); + + VkFramebufferCreateInfo fb_create_info = {}; + fb_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_create_info.renderPass = VkRenderPass(swap_chain->render_pass.id); + fb_create_info.attachmentCount = 1; + fb_create_info.width = surface->width; + fb_create_info.height = surface->height; + fb_create_info.layers = 1; + + VkFramebuffer framebuffer; + for (uint32_t i = 0; i < image_count; i++) { + fb_create_info.pAttachments = &swap_chain->image_views[i]; + err = vkCreateFramebuffer(vk_device, &fb_create_info, nullptr, &framebuffer); + ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE); + + swap_chain->framebuffers.push_back(RDD::FramebufferID(framebuffer)); + } + + // Once everything's been created correctly, indicate the surface no longer needs to be resized. + context_driver->surface_set_needs_resize(swap_chain->surface, false); + + return OK; +} + +RDD::FramebufferID RenderingDeviceDriverVulkan::swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) { + DEV_ASSERT(p_cmd_queue); + DEV_ASSERT(p_swap_chain); + + CommandQueue *command_queue = (CommandQueue *)(p_cmd_queue.id); + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + if ((swap_chain->vk_swapchain == VK_NULL_HANDLE) || context_driver->surface_get_needs_resize(swap_chain->surface)) { + // The surface does not have a valid swap chain or it indicates it requires a resize. + r_resize_required = true; + return FramebufferID(); + } + + VkResult err; + VkSemaphore semaphore = VK_NULL_HANDLE; + uint32_t semaphore_index = 0; + if (command_queue->free_image_semaphores.is_empty()) { + // Add a new semaphore if none are free. + VkSemaphoreCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + err = vkCreateSemaphore(vk_device, &create_info, nullptr, &semaphore); + ERR_FAIL_COND_V(err != VK_SUCCESS, FramebufferID()); + + semaphore_index = command_queue->image_semaphores.size(); + command_queue->image_semaphores.push_back(semaphore); + command_queue->image_semaphores_swap_chains.push_back(swap_chain); + } else { + // Pick a free semaphore. + uint32_t free_index = command_queue->free_image_semaphores.size() - 1; + semaphore_index = command_queue->free_image_semaphores[free_index]; + command_queue->image_semaphores_swap_chains[semaphore_index] = swap_chain; + command_queue->free_image_semaphores.remove_at(free_index); + semaphore = command_queue->image_semaphores[semaphore_index]; + } + + // Store in the swap chain the acquired semaphore. + swap_chain->command_queues_acquired.push_back(command_queue); + swap_chain->command_queues_acquired_semaphores.push_back(semaphore_index); + + err = device_functions.AcquireNextImageKHR(vk_device, swap_chain->vk_swapchain, UINT64_MAX, semaphore, VK_NULL_HANDLE, &swap_chain->image_index); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { + // We choose to treat out of date and suboptimal as the same case, as they both need to be recreated and + // we don't get much use out of presenting a suboptimal image anyway. Either case leaves the semaphore in + // a signaled state that will never finish, so it's necessary to recreate it. + bool semaphore_recreated = _recreate_image_semaphore(command_queue, semaphore_index, true); + ERR_FAIL_COND_V(!semaphore_recreated, FramebufferID()); + + // Swap chain is out of date and must be recreated. + r_resize_required = true; + return FramebufferID(); + } else if (err != VK_SUCCESS) { + // Swap chain failed to present but the reason is unknown. + return FramebufferID(); + } + + // Indicate the command queue should wait on these semaphores on the next submission and that it should + // indicate they're free again on the next fence. + command_queue->pending_semaphores_for_execute.push_back(semaphore_index); + command_queue->pending_semaphores_for_fence.push_back(semaphore_index); + + // Return the corresponding framebuffer to the new current image. + return swap_chain->framebuffers[swap_chain->image_index]; +} + +RDD::RenderPassID RenderingDeviceDriverVulkan::swap_chain_get_render_pass(SwapChainID p_swap_chain) { + DEV_ASSERT(p_swap_chain.id != 0); + + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + return swap_chain->render_pass; +} + +RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p_swap_chain) { + DEV_ASSERT(p_swap_chain.id != 0); + + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + switch (swap_chain->format) { + case VK_FORMAT_B8G8R8A8_UNORM: + return DATA_FORMAT_B8G8R8A8_UNORM; + case VK_FORMAT_R8G8B8A8_UNORM: + return DATA_FORMAT_R8G8B8A8_UNORM; + default: + DEV_ASSERT(false && "Unknown swap chain format."); + return DATA_FORMAT_MAX; + } +} + +void RenderingDeviceDriverVulkan::swap_chain_free(SwapChainID p_swap_chain) { + DEV_ASSERT(p_swap_chain.id != 0); + + SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); + _swap_chain_release(swap_chain); + + if (swap_chain->render_pass.id != 0) { + vkDestroyRenderPass(vk_device, VkRenderPass(swap_chain->render_pass.id), nullptr); + } + + memdelete(swap_chain); +} + /*********************/ /**** FRAMEBUFFER ****/ /*********************/ @@ -1293,8 +2837,8 @@ Vector RenderingDeviceDriverVulkan::shader_compile_binary_from_spirv(Ve return Vector(); } - ERR_FAIL_COND_V_MSG((uint32_t)shader_refl.uniform_sets.size() > limits.maxBoundDescriptorSets, Vector(), - "Number of uniform sets is larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); + ERR_FAIL_COND_V_MSG((uint32_t)shader_refl.uniform_sets.size() > physical_device_properties.limits.maxBoundDescriptorSets, Vector(), + "Number of uniform sets is larger than what is supported by the hardware (" + itos(physical_device_properties.limits.maxBoundDescriptorSets) + ")."); // Collect reflection data into binary data. ShaderBinary::Data binary_data; @@ -2274,12 +3818,13 @@ bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector &p { VkPipelineCacheCreateInfo cache_info = {}; cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - if (context->get_pipeline_cache_control_support()) { - cache_info.flags = VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT; - } cache_info.initialDataSize = pipelines_cache.buffer.size() - sizeof(PipelineCacheHeader); cache_info.pInitialData = pipelines_cache.buffer.ptr() + sizeof(PipelineCacheHeader); + if (pipeline_cache_control_support) { + cache_info.flags = VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT; + } + VkResult err = vkCreatePipelineCache(vk_device, &cache_info, nullptr, &pipelines_cache.vk_cache); if (err != VK_SUCCESS) { WARN_PRINT("vkCreatePipelinecache failed with error " + itos(err) + "."); @@ -2294,6 +3839,7 @@ void RenderingDeviceDriverVulkan::pipeline_cache_free() { DEV_ASSERT(pipelines_cache.vk_cache); vkDestroyPipelineCache(vk_device, pipelines_cache.vk_cache, nullptr); + pipelines_cache.vk_cache = VK_NULL_HANDLE; DEV_ASSERT(caching_instance_count > 0); caching_instance_count--; @@ -2412,7 +3958,7 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::render_pass_create(VectorViewget_vrs_capabilities().attachment_vrs_supported && p_subpasses[i].vrs_reference.attachment != AttachmentReference::UNUSED) { + if (vrs_capabilities.attachment_vrs_supported && p_subpasses[i].vrs_reference.attachment != AttachmentReference::UNUSED) { VkAttachmentReference2KHR *vk_subpass_vrs_attachment = ALLOCA_SINGLE(VkAttachmentReference2KHR); *vk_subpass_vrs_attachment = {}; vk_subpass_vrs_attachment->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; @@ -2423,8 +3969,8 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::render_pass_create(VectorViewsType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR; vk_vrs_info->pFragmentShadingRateAttachment = vk_subpass_vrs_attachment; - vk_vrs_info->shadingRateAttachmentTexelSize.width = context->get_vrs_capabilities().texel_size.x; - vk_vrs_info->shadingRateAttachmentTexelSize.height = context->get_vrs_capabilities().texel_size.y; + vk_vrs_info->shadingRateAttachmentTexelSize.width = vrs_capabilities.texel_size.x; + vk_vrs_info->shadingRateAttachmentTexelSize.height = vrs_capabilities.texel_size.y; vk_subpasses[i].pNext = vk_vrs_info; } @@ -2454,8 +4000,8 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::render_pass_create(VectorView 1 && !context->supports_renderpass2()) { - // This is only required when using vkCreateRenderPass. + if (p_view_count > 1 && device_functions.CreateRenderPass2KHR == nullptr) { + // This is only required when not using vkCreateRenderPass2. // We add it if vkCreateRenderPass2KHR is not supported, // resulting this in being passed to our vkCreateRenderPass fallback. @@ -2476,7 +4022,7 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::render_pass_create(VectorViewvkCreateRenderPass2KHR(vk_device, &create_info, nullptr, &vk_render_pass); + VkResult res = _create_render_pass(vk_device, &create_info, nullptr, &vk_render_pass); ERR_FAIL_COND_V_MSG(res, RenderPassID(), "vkCreateRenderPass2KHR failed with error " + itos(res) + "."); return RenderPassID(vk_render_pass); @@ -2738,7 +4284,7 @@ RDD::PipelineID RenderingDeviceDriverVulkan::render_pipeline_create( // Tessellation. VkPipelineTessellationStateCreateInfo tessellation_create_info = {}; tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; - ERR_FAIL_COND_V(limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize), PipelineID()); + ERR_FAIL_COND_V(physical_device_properties.limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > physical_device_properties.limits.maxTessellationPatchSize), PipelineID()); tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points; // Viewport. @@ -2900,7 +4446,7 @@ RDD::PipelineID RenderingDeviceDriverVulkan::render_pipeline_create( void *graphics_pipeline_nextptr = nullptr; - if (context->get_vrs_capabilities().attachment_vrs_supported) { + if (vrs_capabilities.attachment_vrs_supported) { // If VRS is used, this defines how the different VRS types are combined. // combinerOps[0] decides how we use the output of pipeline and primitive (drawcall) VRS. // combinerOps[1] decides how we use the output of combinerOps[0] and our attachment VRS. @@ -3086,7 +4632,7 @@ uint64_t RenderingDeviceDriverVulkan::timestamp_query_result_to_time(uint64_t p_ uint64_t shift_bits = 16; uint64_t h = 0, l = 0; - mult64to128(p_result, uint64_t(double(context->get_device_limits().timestampPeriod) * double(1 << shift_bits)), h, l); + mult64to128(p_result, uint64_t(double(physical_device_properties.limits.timestampPeriod) * double(1 << shift_bits)), h, l); l >>= shift_bits; l |= h << (64 - shift_bits); @@ -3106,6 +4652,7 @@ void RenderingDeviceDriverVulkan::command_timestamp_write(CommandBufferID p_cmd_ /****************/ void RenderingDeviceDriverVulkan::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) { + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; @@ -3114,38 +4661,24 @@ void RenderingDeviceDriverVulkan::command_begin_label(CommandBufferID p_cmd_buff label.color[1] = p_color[1]; label.color[2] = p_color[2]; label.color[3] = p_color[3]; - vkCmdBeginDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id, &label); + functions.CmdBeginDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id, &label); } void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer) { - vkCmdEndDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id); -} - -/****************/ -/**** SCREEN ****/ -/****************/ - -RDD::DataFormat RenderingDeviceDriverVulkan::screen_get_format() { - // Very hacky, but not used often per frame so I guess ok. - VkFormat vk_format = context->get_screen_format(); - DataFormat format = DATA_FORMAT_MAX; - for (int i = 0; i < DATA_FORMAT_MAX; i++) { - if (vk_format == RD_TO_VK_FORMAT[i]) { - format = DataFormat(i); - break; - } - } - return format; + const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get(); + functions.CmdEndDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id); } /********************/ /**** SUBMISSION ****/ /********************/ -void RenderingDeviceDriverVulkan::begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) { +void RenderingDeviceDriverVulkan::begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) { + // Per-frame segments are not required in Vulkan. } void RenderingDeviceDriverVulkan::end_segment() { + // Per-frame segments are not required in Vulkan. } /**************/ @@ -3157,33 +4690,33 @@ void RenderingDeviceDriverVulkan::set_object_name(ObjectType p_type, ID p_driver case OBJECT_TYPE_TEXTURE: { const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; if (tex_info->allocation.handle) { - context->set_object_name(VK_OBJECT_TYPE_IMAGE, (uint64_t)tex_info->vk_view_create_info.image, p_name); + _set_object_name(VK_OBJECT_TYPE_IMAGE, (uint64_t)tex_info->vk_view_create_info.image, p_name); } - context->set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)tex_info->vk_view, p_name + " View"); + _set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)tex_info->vk_view, p_name + " View"); } break; case OBJECT_TYPE_SAMPLER: { - context->set_object_name(VK_OBJECT_TYPE_SAMPLER, p_driver_id.id, p_name); + _set_object_name(VK_OBJECT_TYPE_SAMPLER, p_driver_id.id, p_name); } break; case OBJECT_TYPE_BUFFER: { const BufferInfo *buf_info = (const BufferInfo *)p_driver_id.id; - context->set_object_name(VK_OBJECT_TYPE_BUFFER, (uint64_t)buf_info->vk_buffer, p_name); + _set_object_name(VK_OBJECT_TYPE_BUFFER, (uint64_t)buf_info->vk_buffer, p_name); if (buf_info->vk_view) { - context->set_object_name(VK_OBJECT_TYPE_BUFFER_VIEW, (uint64_t)buf_info->vk_view, p_name + " View"); + _set_object_name(VK_OBJECT_TYPE_BUFFER_VIEW, (uint64_t)buf_info->vk_view, p_name + " View"); } } break; case OBJECT_TYPE_SHADER: { const ShaderInfo *shader_info = (const ShaderInfo *)p_driver_id.id; for (uint32_t i = 0; i < shader_info->vk_descriptor_set_layouts.size(); i++) { - context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)shader_info->vk_descriptor_set_layouts[i], p_name); + _set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, (uint64_t)shader_info->vk_descriptor_set_layouts[i], p_name); } - context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, (uint64_t)shader_info->vk_pipeline_layout, p_name + " Pipeline Layout"); + _set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, (uint64_t)shader_info->vk_pipeline_layout, p_name + " Pipeline Layout"); } break; case OBJECT_TYPE_UNIFORM_SET: { const UniformSetInfo *usi = (const UniformSetInfo *)p_driver_id.id; - context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t)usi->vk_descriptor_set, p_name); + _set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t)usi->vk_descriptor_set, p_name); } break; case OBJECT_TYPE_PIPELINE: { - context->set_object_name(VK_OBJECT_TYPE_PIPELINE, (uint64_t)p_driver_id.id, p_name); + _set_object_name(VK_OBJECT_TYPE_PIPELINE, (uint64_t)p_driver_id.id, p_name); } break; default: { DEV_ASSERT(false); @@ -3197,16 +4730,17 @@ uint64_t RenderingDeviceDriverVulkan::get_resource_native_handle(DriverResource return (uint64_t)vk_device; } case DRIVER_RESOURCE_PHYSICAL_DEVICE: { - return (uint64_t)context->get_physical_device(); + return (uint64_t)physical_device; } case DRIVER_RESOURCE_TOPMOST_OBJECT: { - return (uint64_t)context->get_instance(); + return (uint64_t)context_driver->instance_get(); } case DRIVER_RESOURCE_COMMAND_QUEUE: { - return (uint64_t)context->get_graphics_queue(); + const CommandQueue *queue_info = (const CommandQueue *)p_driver_id.id; + return (uint64_t)queue_families[queue_info->queue_family][queue_info->queue_index].queue; } case DRIVER_RESOURCE_QUEUE_FAMILY: { - return context->get_graphics_queue_family_index(); + return uint32_t(p_driver_id.id) - 1; } case DRIVER_RESOURCE_TEXTURE: { const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id; @@ -3240,6 +4774,7 @@ uint64_t RenderingDeviceDriverVulkan::get_total_memory_used() { } uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) { + const VkPhysicalDeviceLimits &limits = physical_device_properties.limits; switch (p_limit) { case LIMIT_MAX_BOUND_UNIFORM_SETS: return limits.maxBoundDescriptorSets; @@ -3313,30 +4848,20 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) { return limits.maxViewportDimensions[0]; case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y: return limits.maxViewportDimensions[1]; - case LIMIT_SUBGROUP_SIZE: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_SIZE: return subgroup_capabilities.size; - } - case LIMIT_SUBGROUP_MIN_SIZE: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_MIN_SIZE: return subgroup_capabilities.min_size; - } - case LIMIT_SUBGROUP_MAX_SIZE: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_MAX_SIZE: return subgroup_capabilities.max_size; - } - case LIMIT_SUBGROUP_IN_SHADERS: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_IN_SHADERS: return subgroup_capabilities.supported_stages_flags_rd(); - } - case LIMIT_SUBGROUP_OPERATIONS: { - VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + case LIMIT_SUBGROUP_OPERATIONS: return subgroup_capabilities.supported_operations_flags_rd(); - } case LIMIT_VRS_TEXEL_WIDTH: - return context->get_vrs_capabilities().texel_size.x; + return vrs_capabilities.texel_size.x; case LIMIT_VRS_TEXEL_HEIGHT: - return context->get_vrs_capabilities().texel_size.y; + return vrs_capabilities.texel_size.y; default: ERR_FAIL_V(0); } @@ -3345,7 +4870,7 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) { uint64_t RenderingDeviceDriverVulkan::api_trait_get(ApiTrait p_trait) { switch (p_trait) { case API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT: - return (uint64_t)MAX((uint64_t)16, limits.optimalBufferCopyOffsetAlignment); + return (uint64_t)MAX((uint64_t)16, physical_device_properties.limits.optimalBufferCopyOffsetAlignment); case API_TRAIT_SHADER_CHANGE_INVALIDATION: return (uint64_t)SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE; default: @@ -3355,57 +4880,46 @@ uint64_t RenderingDeviceDriverVulkan::api_trait_get(ApiTrait p_trait) { bool RenderingDeviceDriverVulkan::has_feature(Features p_feature) { switch (p_feature) { - case SUPPORTS_MULTIVIEW: { - MultiviewCapabilities multiview_capabilies = context->get_multiview_capabilities(); - return multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; - } break; - case SUPPORTS_FSR_HALF_FLOAT: { - return context->get_shader_capabilities().shader_float16_is_supported && context->get_physical_device_features().shaderInt16 && context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; - } break; - case SUPPORTS_ATTACHMENT_VRS: { - VulkanContext::VRSCapabilities vrs_capabilities = context->get_vrs_capabilities(); - return vrs_capabilities.attachment_vrs_supported && context->get_physical_device_features().shaderStorageImageExtendedFormats; - } break; - case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: { + case SUPPORTS_MULTIVIEW: + return multiview_capabilities.is_supported && multiview_capabilities.max_view_count > 1; + case SUPPORTS_FSR_HALF_FLOAT: + return shader_capabilities.shader_float16_is_supported && physical_device_features.shaderInt16 && storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + case SUPPORTS_ATTACHMENT_VRS: + return vrs_capabilities.attachment_vrs_supported && physical_device_features.shaderStorageImageExtendedFormats; + case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; - } break; - default: { + default: return false; - } } } const RDD::MultiviewCapabilities &RenderingDeviceDriverVulkan::get_multiview_capabilities() { - return context->get_multiview_capabilities(); + return multiview_capabilities; +} + +String RenderingDeviceDriverVulkan::get_api_name() const { + return "Vulkan"; +} + +String RenderingDeviceDriverVulkan::get_api_version() const { + uint32_t api_version = physical_device_properties.apiVersion; + return vformat("%d.%d.%d", VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version)); +} + +String RenderingDeviceDriverVulkan::get_pipeline_cache_uuid() const { + return pipeline_cache_id; +} + +const RDD::Capabilities &RenderingDeviceDriverVulkan::get_capabilities() const { + return device_capabilities; } /******************/ -RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(VulkanContext *p_context, VkDevice p_vk_device) : - context(p_context), - vk_device(p_vk_device) { - VmaAllocatorCreateInfo allocator_info = {}; - allocator_info.physicalDevice = context->get_physical_device(); - allocator_info.device = vk_device; - allocator_info.instance = context->get_instance(); - VkResult err = vmaCreateAllocator(&allocator_info, &allocator); - ERR_FAIL_COND_MSG(err, "vmaCreateAllocator failed with error " + itos(err) + "."); +RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverVulkan *p_context_driver) { + DEV_ASSERT(p_context_driver != nullptr); - max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool"); - - VkPhysicalDeviceProperties props = {}; - vkGetPhysicalDeviceProperties(context->get_physical_device(), &props); - pipelines_cache.buffer.resize(sizeof(PipelineCacheHeader)); - PipelineCacheHeader *header = (PipelineCacheHeader *)pipelines_cache.buffer.ptrw(); - *header = {}; - header->magic = 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE; - header->device_id = props.deviceID; - header->vendor_id = props.vendorID; - header->driver_version = props.driverVersion; - memcpy(header->uuid, props.pipelineCacheUUID, VK_UUID_SIZE); - header->driver_abi = sizeof(void *); - - limits = context->get_device_limits(); + context_driver = p_context_driver; } RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() { @@ -3415,4 +4929,8 @@ RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() { small_allocs_pools.remove(E); } vmaDestroyAllocator(allocator); + + if (vk_device != VK_NULL_HANDLE) { + vkDestroyDevice(vk_device, nullptr); + } } diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 1edee6b76e1..4abaeecd119 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -33,6 +33,7 @@ #include "core/templates/hash_map.h" #include "core/templates/paged_allocator.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #include "servers/rendering/rendering_device_driver.h" #ifdef DEBUG_ENABLED @@ -48,8 +49,6 @@ #include #endif -class VulkanContext; - // Design principles: // - Vulkan structs are zero-initialized and fields not requiring a non-zero value are omitted (except in cases where expresivity reasons apply). class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { @@ -57,9 +56,99 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver { /**** GENERIC ****/ /*****************/ - VulkanContext *context = nullptr; - VkDevice vk_device = VK_NULL_HANDLE; // Owned by the context. + struct CommandQueue; + struct SwapChain; + struct Queue { + VkQueue queue = VK_NULL_HANDLE; + uint32_t virtual_count = 0; + BinaryMutex submit_mutex; + }; + + struct SubgroupCapabilities { + uint32_t size = 0; + uint32_t min_size = 0; + uint32_t max_size = 0; + VkShaderStageFlags supported_stages = 0; + VkSubgroupFeatureFlags supported_operations = 0; + VkBool32 quad_operations_in_all_stages = false; + bool size_control_is_supported = false; + + uint32_t supported_stages_flags_rd() const; + String supported_stages_desc() const; + uint32_t supported_operations_flags_rd() const; + String supported_operations_desc() const; + }; + + struct VRSCapabilities { + bool pipeline_vrs_supported = false; // We can specify our fragment rate on a pipeline level. + bool primitive_vrs_supported = false; // We can specify our fragment rate on each drawcall. + bool attachment_vrs_supported = false; // We can provide a density map attachment on our framebuffer. + + Size2i min_texel_size; + Size2i max_texel_size; + + Size2i texel_size; // The texel size we'll use + }; + + struct ShaderCapabilities { + bool shader_float16_is_supported = false; + bool shader_int8_is_supported = false; + }; + + struct StorageBufferCapabilities { + bool storage_buffer_16_bit_access_is_supported = false; + bool uniform_and_storage_buffer_16_bit_access_is_supported = false; + bool storage_push_constant_16_is_supported = false; + bool storage_input_output_16 = false; + }; + + struct DeviceFunctions { + PFN_vkCreateSwapchainKHR CreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR DestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR AcquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR QueuePresentKHR = nullptr; + PFN_vkCreateRenderPass2KHR CreateRenderPass2KHR = nullptr; + }; + + VkDevice vk_device = VK_NULL_HANDLE; + RenderingContextDriverVulkan *context_driver = nullptr; + RenderingContextDriver::Device context_device = {}; + VkPhysicalDevice physical_device = VK_NULL_HANDLE; + VkPhysicalDeviceProperties physical_device_properties = {}; + VkPhysicalDeviceFeatures physical_device_features = {}; + VkPhysicalDeviceFeatures requested_device_features = {}; + HashMap requested_device_extensions; + HashSet enabled_device_extension_names; + TightLocalVector> queue_families; + TightLocalVector queue_family_properties; + RDD::Capabilities device_capabilities; + SubgroupCapabilities subgroup_capabilities; + MultiviewCapabilities multiview_capabilities; + VRSCapabilities vrs_capabilities; + ShaderCapabilities shader_capabilities; + StorageBufferCapabilities storage_buffer_capabilities; + bool pipeline_cache_control_support = false; + DeviceFunctions device_functions; + + void _register_requested_device_extension(const CharString &p_extension_name, bool p_required); + Error _initialize_device_extensions(); + Error _check_device_features(); + Error _check_device_capabilities(); + Error _add_queue_create_info(LocalVector &r_queue_create_info); + Error _initialize_device(const LocalVector &p_queue_create_info); + Error _initialize_allocator(); + Error _initialize_pipeline_cache(); + VkResult _create_render_pass(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass); + bool _release_image_semaphore(CommandQueue *p_command_queue, uint32_t p_semaphore_index, bool p_release_on_swap_chain); + bool _recreate_image_semaphore(CommandQueue *p_command_queue, uint32_t p_semaphore_index, bool p_release_on_swap_chain); + void _set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name); + +public: + Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final; + +private: /****************/ /**** MEMORY ****/ /****************/ @@ -154,32 +243,104 @@ public: VectorView p_buffer_barriers, VectorView p_texture_barriers) override final; - /*************************/ - /**** COMMAND BUFFERS ****/ - /*************************/ -private: -#ifdef DEBUG_ENABLED - // Vulkan doesn't need to know if the command buffers created in a pool - // will be primary or secondary, but RDD works like that, so we will enforce. + /****************/ + /**** FENCES ****/ + /****************/ - HashSet secondary_cmd_pools; - HashSet secondary_cmd_buffers; -#endif +private: + struct Fence { + VkFence vk_fence = VK_NULL_HANDLE; + CommandQueue *queue_signaled_from = nullptr; + }; public: + virtual FenceID fence_create() override final; + virtual Error fence_wait(FenceID p_fence) override final; + virtual void fence_free(FenceID p_fence) override final; + + /********************/ + /**** SEMAPHORES ****/ + /********************/ + + virtual SemaphoreID semaphore_create() override final; + virtual void semaphore_free(SemaphoreID p_semaphore) override final; + + /******************/ + /**** COMMANDS ****/ + /******************/ + + // ----- QUEUE FAMILY ----- + + virtual CommandQueueFamilyID command_queue_family_get(BitField p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override final; + + // ----- QUEUE ----- +private: + struct CommandQueue { + LocalVector image_semaphores; + LocalVector image_semaphores_swap_chains; + LocalVector pending_semaphores_for_execute; + LocalVector pending_semaphores_for_fence; + LocalVector free_image_semaphores; + LocalVector> image_semaphores_for_fences; + uint32_t queue_family = 0; + uint32_t queue_index = 0; + }; + +public: + virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final; + virtual Error command_queue_execute(CommandQueueID p_cmd_queue, VectorView p_cmd_buffers, VectorView p_wait_semaphores, VectorView p_signal_semaphores, FenceID p_signal_fence) override final; + virtual Error command_queue_present(CommandQueueID p_cmd_queue, VectorView p_swap_chains, VectorView p_wait_semaphores) override final; + virtual void command_queue_free(CommandQueueID p_cmd_queue) override final; + +private: // ----- POOL ----- - virtual CommandPoolID command_pool_create(CommandBufferType p_cmd_buffer_type) override final; + struct CommandPool { + VkCommandPool vk_command_pool = VK_NULL_HANDLE; + CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY; + }; + +public: + virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final; virtual void command_pool_free(CommandPoolID p_cmd_pool) override final; // ----- BUFFER ----- - virtual CommandBufferID command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) override final; + virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final; virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final; virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final; virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final; virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView p_secondary_cmd_buffers) override final; + /********************/ + /**** SWAP CHAIN ****/ + /********************/ + +private: + struct SwapChain { + VkSwapchainKHR vk_swapchain = VK_NULL_HANDLE; + RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID(); + VkFormat format = VK_FORMAT_UNDEFINED; + VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + TightLocalVector images; + TightLocalVector image_views; + TightLocalVector framebuffers; + LocalVector command_queues_acquired; + LocalVector command_queues_acquired_semaphores; + RenderPassID render_pass; + uint32_t image_index = 0; + }; + + void _swap_chain_release(SwapChain *p_swap_chain); + +public: + virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override final; + virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final; + virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final; + virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final; + virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final; + virtual void swap_chain_free(SwapChainID p_swap_chain) override final; + /*********************/ /**** FRAMEBUFFER ****/ /*********************/ @@ -329,6 +490,7 @@ private: static int caching_instance_count; PipelineCache pipelines_cache; + String pipeline_cache_id; public: virtual void pipeline_free(PipelineID p_pipeline) override final; @@ -439,25 +601,17 @@ public: virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final; virtual void command_end_label(CommandBufferID p_cmd_buffer) override final; - /****************/ - /**** SCREEN ****/ - /****************/ - - virtual DataFormat screen_get_format() override final; - /********************/ /**** SUBMISSION ****/ /********************/ - virtual void begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) override final; + virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final; virtual void end_segment() override final; /**************/ /**** MISC ****/ /**************/ - VkPhysicalDeviceLimits limits = {}; - virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final; virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final; virtual uint64_t get_total_memory_used() override final; @@ -465,6 +619,10 @@ public: virtual uint64_t api_trait_get(ApiTrait p_trait) override final; virtual bool has_feature(Features p_feature) override final; virtual const MultiviewCapabilities &get_multiview_capabilities() override final; + virtual String get_api_name() const override final; + virtual String get_api_version() const override final; + virtual String get_pipeline_cache_uuid() const override final; + virtual const Capabilities &get_capabilities() const override final; private: /*********************/ @@ -482,7 +640,7 @@ private: /******************/ public: - RenderingDeviceDriverVulkan(VulkanContext *p_context, VkDevice p_vk_device); + RenderingDeviceDriverVulkan(RenderingContextDriverVulkan *p_context_driver); virtual ~RenderingDeviceDriverVulkan(); }; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp deleted file mode 100644 index 7db2a9cd661..00000000000 --- a/drivers/vulkan/vulkan_context.cpp +++ /dev/null @@ -1,2915 +0,0 @@ -/**************************************************************************/ -/* vulkan_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 "vulkan_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 "vk_enum_string_helper.h" - -#include -#include -#include - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) -#define APP_SHORT_NAME "GodotEngine" - -VulkanHooks *VulkanContext::vulkan_hooks = nullptr; - -Vector VulkanContext::_convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs) { - Vector att_refs; - - if (p_refs != nullptr) { - for (uint32_t i = 0; i < p_count; i++) { - // We lose aspectMask in this conversion but we don't use it currently. - - VkAttachmentReference ref = { - p_refs[i].attachment, /* attachment */ - p_refs[i].layout /* layout */ - }; - - att_refs.push_back(ref); - } - } - - return att_refs; -} - -VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { - if (is_device_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { - if (fpCreateRenderPass2KHR == nullptr) { - fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetDeviceProcAddr(p_device, "vkCreateRenderPass2KHR"); - } - - if (fpCreateRenderPass2KHR == nullptr) { - return VK_ERROR_EXTENSION_NOT_PRESENT; - } else { - return (fpCreateRenderPass2KHR)(p_device, p_create_info, p_allocator, p_render_pass); - } - } else { - // need to fall back on vkCreateRenderPass - - const void *next = p_create_info->pNext; // ATM we only support multiview which should work if supported. - - Vector attachments; - for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { - // Basically the old layout just misses type and next. - VkAttachmentDescription att = { - p_create_info->pAttachments[i].flags, /* flags */ - p_create_info->pAttachments[i].format, /* format */ - p_create_info->pAttachments[i].samples, /* samples */ - p_create_info->pAttachments[i].loadOp, /* loadOp */ - p_create_info->pAttachments[i].storeOp, /* storeOp */ - p_create_info->pAttachments[i].stencilLoadOp, /* stencilLoadOp */ - p_create_info->pAttachments[i].stencilStoreOp, /* stencilStoreOp */ - p_create_info->pAttachments[i].initialLayout, /* initialLayout */ - p_create_info->pAttachments[i].finalLayout /* finalLayout */ - }; - - attachments.push_back(att); - } - - Vector> attachment_references; - Vector subpasses; - for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { - // Here we need to do more, again it's just stripping out type and next - // but we have VkAttachmentReference2 to convert to VkAttachmentReference. - // Also viewmask is not supported but we don't use it outside of multiview. - - Vector input_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].inputAttachmentCount, p_create_info->pSubpasses[i].pInputAttachments); - Vector color_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pColorAttachments); - Vector resolve_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pResolveAttachments); - Vector depth_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pDepthStencilAttachment); - - VkSubpassDescription subpass = { - p_create_info->pSubpasses[i].flags, /* flags */ - p_create_info->pSubpasses[i].pipelineBindPoint, /* pipelineBindPoint */ - p_create_info->pSubpasses[i].inputAttachmentCount, /* inputAttachmentCount */ - input_attachments.size() == 0 ? nullptr : input_attachments.ptr(), /* pInputAttachments */ - p_create_info->pSubpasses[i].colorAttachmentCount, /* colorAttachmentCount */ - color_attachments.size() == 0 ? nullptr : color_attachments.ptr(), /* pColorAttachments */ - resolve_attachments.size() == 0 ? nullptr : resolve_attachments.ptr(), /* pResolveAttachments */ - depth_attachments.size() == 0 ? nullptr : depth_attachments.ptr(), /* pDepthStencilAttachment */ - p_create_info->pSubpasses[i].preserveAttachmentCount, /* preserveAttachmentCount */ - p_create_info->pSubpasses[i].pPreserveAttachments /* pPreserveAttachments */ - }; - attachment_references.push_back(input_attachments); - attachment_references.push_back(color_attachments); - attachment_references.push_back(resolve_attachments); - attachment_references.push_back(depth_attachments); - - subpasses.push_back(subpass); - } - - Vector dependencies; - for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { - // We lose viewOffset here but again I don't believe we use this anywhere. - VkSubpassDependency dep = { - p_create_info->pDependencies[i].srcSubpass, /* srcSubpass */ - p_create_info->pDependencies[i].dstSubpass, /* dstSubpass */ - p_create_info->pDependencies[i].srcStageMask, /* srcStageMask */ - p_create_info->pDependencies[i].dstStageMask, /* dstStageMask */ - p_create_info->pDependencies[i].srcAccessMask, /* srcAccessMask */ - p_create_info->pDependencies[i].dstAccessMask, /* dstAccessMask */ - p_create_info->pDependencies[i].dependencyFlags, /* dependencyFlags */ - }; - - dependencies.push_back(dep); - } - - // CorrelatedViewMask is not supported in vkCreateRenderPass but we - // currently only use this for multiview. - // We'll need to look into this. - - VkRenderPassCreateInfo create_info = { - VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, /* sType */ - next, /* pNext*/ - p_create_info->flags, /* flags */ - (uint32_t)attachments.size(), /* attachmentCount */ - attachments.ptr(), /* pAttachments */ - (uint32_t)subpasses.size(), /* subpassCount */ - subpasses.ptr(), /* pSubpasses */ - (uint32_t)dependencies.size(), /* */ - dependencies.ptr(), /* */ - }; - - return vkCreateRenderPass(device, &create_info, p_allocator, p_render_pass); - } -} - -VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, - void *pUserData) { - // This error needs to be ignored because the AMD allocator will mix up memory types on IGP processors. - if (strstr(pCallbackData->pMessage, "Mapping an image with layout") != nullptr && - strstr(pCallbackData->pMessage, "can result in undefined behavior if this memory is used by the device") != nullptr) { - return VK_FALSE; - } - // This needs to be ignored because Validator is wrong here. - if (strstr(pCallbackData->pMessage, "Invalid SPIR-V binary version 1.3") != nullptr) { - return VK_FALSE; - } - // This needs to be ignored because Validator is wrong here. - if (strstr(pCallbackData->pMessage, "Shader requires flag") != nullptr) { - return VK_FALSE; - } - - // This needs to be ignored because Validator is wrong here. - if (strstr(pCallbackData->pMessage, "SPIR-V module not valid: Pointer operand") != nullptr && - strstr(pCallbackData->pMessage, "must be a memory object") != nullptr) { - return VK_FALSE; - } - - if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { - return VK_FALSE; - } - - String type_string; - switch (messageType) { - case (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT): - type_string = "GENERAL"; - break; - case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT): - type_string = "VALIDATION"; - break; - case (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): - type_string = "PERFORMANCE"; - break; - case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): - type_string = "VALIDATION|PERFORMANCE"; - break; - } - - String objects_string; - if (pCallbackData->objectCount > 0) { - objects_string = "\n\tObjects - " + String::num_int64(pCallbackData->objectCount); - for (uint32_t object = 0; object < pCallbackData->objectCount; ++object) { - objects_string += - "\n\t\tObject[" + String::num_int64(object) + "]" + - " - " + string_VkObjectType(pCallbackData->pObjects[object].objectType) + - ", Handle " + String::num_int64(pCallbackData->pObjects[object].objectHandle); - if (nullptr != pCallbackData->pObjects[object].pObjectName && strlen(pCallbackData->pObjects[object].pObjectName) > 0) { - objects_string += ", Name \"" + String(pCallbackData->pObjects[object].pObjectName) + "\""; - } - } - } - - String labels_string; - if (pCallbackData->cmdBufLabelCount > 0) { - labels_string = "\n\tCommand Buffer Labels - " + String::num_int64(pCallbackData->cmdBufLabelCount); - for (uint32_t cmd_buf_label = 0; cmd_buf_label < pCallbackData->cmdBufLabelCount; ++cmd_buf_label) { - labels_string += - "\n\t\tLabel[" + String::num_int64(cmd_buf_label) + "]" + - " - " + pCallbackData->pCmdBufLabels[cmd_buf_label].pLabelName + - "{ "; - for (int color_idx = 0; color_idx < 4; ++color_idx) { - labels_string += String::num(pCallbackData->pCmdBufLabels[cmd_buf_label].color[color_idx]); - if (color_idx < 3) { - labels_string += ", "; - } - } - labels_string += " }"; - } - } - - String error_message(type_string + - " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) + - " | Message Id Name: " + pCallbackData->pMessageIdName + - "\n\t" + pCallbackData->pMessage + - objects_string + labels_string); - - // Convert VK severity to our own log macros. - switch (messageSeverity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - print_verbose(error_message); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - print_line(error_message); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN_PRINT(error_message); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - 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; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT: - break; // Shouldn't happen, only handling to make compilers happy. - } - - return VK_FALSE; -} - -VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char *pLayerPrefix, - const char *pMessage, - void *pUserData) { - String debugMessage = String("Vulkan Debug Report: object - ") + - String::num_int64(object) + "\n" + pMessage; - - switch (flags) { - case VK_DEBUG_REPORT_DEBUG_BIT_EXT: - case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: - print_line(debugMessage); - break; - case VK_DEBUG_REPORT_WARNING_BIT_EXT: - case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: - WARN_PRINT(debugMessage); - break; - case VK_DEBUG_REPORT_ERROR_BIT_EXT: - ERR_PRINT(debugMessage); - break; - } - - return VK_FALSE; -} - -VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers) { - for (uint32_t i = 0; i < check_count; i++) { - VkBool32 found = 0; - for (uint32_t j = 0; j < layer_count; j++) { - if (!strcmp(check_names[i], layers[j].layerName)) { - found = 1; - break; - } - } - if (!found) { - WARN_PRINT("Can't find layer: " + String(check_names[i])); - return 0; - } - } - return 1; -} - -Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { - static const LocalVector> instance_validation_layers_alt{ - // Preferred set of validation layers. - { "VK_LAYER_KHRONOS_validation" }, - - // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers. - { "VK_LAYER_LUNARG_standard_validation" }, - - // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers. - { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" } - }; - - // Clear out-arguments. - *count = 0; - if (names != nullptr) { - *names = nullptr; - } - - VkResult err; - uint32_t instance_layer_count; - - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); - if (err) { - ERR_FAIL_V(ERR_CANT_CREATE); - } - - if (instance_layer_count < 1) { - return OK; - } - - VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); - err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); - if (err) { - free(instance_layers); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - for (const LocalVector &layer : instance_validation_layers_alt) { - if (_check_layers(layer.size(), layer.ptr(), instance_layer_count, instance_layers)) { - *count = layer.size(); - if (names != nullptr) { - *names = layer.ptr(); - } - break; - } - } - - free(instance_layers); - - return OK; -} - -typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); - -Error VulkanContext::_obtain_vulkan_version() { - // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description - // For Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. - _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); - if (func != nullptr) { - uint32_t api_version; - VkResult res = func(&api_version); - if (res == VK_SUCCESS) { - instance_api_version = api_version; - } else { - // According to the documentation this shouldn't fail with anything except a memory allocation error - // in which case we're in deep trouble anyway. - ERR_FAIL_V(ERR_CANT_CREATE); - } - } else { - print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0."); - instance_api_version = VK_API_VERSION_1_0; - } - - return OK; -} - -bool VulkanContext::instance_extensions_initialized = false; -HashMap VulkanContext::requested_instance_extensions; - -void VulkanContext::register_requested_instance_extension(const CharString &extension_name, bool p_required) { - ERR_FAIL_COND_MSG(instance_extensions_initialized, "You can only registered extensions before the Vulkan instance is created"); - ERR_FAIL_COND(requested_instance_extensions.has(extension_name)); - - requested_instance_extensions[extension_name] = p_required; -} - -Error VulkanContext::_initialize_instance_extensions() { - enabled_instance_extension_names.clear(); - - // Make sure our core extensions are here - register_requested_instance_extension(VK_KHR_SURFACE_EXTENSION_NAME, true); - if (_get_platform_surface_extension()) { - register_requested_instance_extension(_get_platform_surface_extension(), true); - } - - if (_use_validation_layers()) { - register_requested_instance_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false); - } - - // This extension allows us to use the properties2 features to query additional device capabilities - register_requested_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); - - // Only enable debug utils in verbose mode or DEV_ENABLED. - // End users would get spammed with messages of varying verbosity due to the - // mess that thirdparty layers/extensions and drivers seem to leave in their - // wake, making the Windows registry a bottomless pit of broken layer JSON. -#ifdef DEV_ENABLED - bool want_debug_utils = true; -#else - bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); -#endif - if (want_debug_utils) { - register_requested_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false); - } - - // Load instance extensions that are available... - uint32_t instance_extension_count = 0; - VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); - ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); - ERR_FAIL_COND_V_MSG(instance_extension_count == 0, ERR_CANT_CREATE, "No instance extensions found, is a driver installed?"); - - VkExtensionProperties *instance_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * instance_extension_count); - err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions); - if (err != VK_SUCCESS && err != VK_INCOMPLETE) { - free(instance_extensions); - ERR_FAIL_V(ERR_CANT_CREATE); - } -#ifdef DEV_ENABLED - for (uint32_t i = 0; i < instance_extension_count; i++) { - print_verbose(String("VULKAN: Found instance extension ") + String::utf8(instance_extensions[i].extensionName)); - } -#endif - - // Enable all extensions that are supported and requested - for (uint32_t i = 0; i < instance_extension_count; i++) { - CharString extension_name(instance_extensions[i].extensionName); - if (requested_instance_extensions.has(extension_name)) { - enabled_instance_extension_names.insert(extension_name); - } - } - - // Now check our requested extensions - for (KeyValue &requested_extension : requested_instance_extensions) { - if (!enabled_instance_extension_names.has(requested_extension.key)) { - if (requested_extension.value) { - free(instance_extensions); - ERR_FAIL_V_MSG(ERR_BUG, String("Required extension ") + String::utf8(requested_extension.key) + String(" not found, is a driver installed?")); - } else { - print_verbose(String("Optional extension ") + String::utf8(requested_extension.key) + String(" not found")); - } - } - } - - free(instance_extensions); - - instance_extensions_initialized = true; - return OK; -} - -bool VulkanContext::device_extensions_initialized = false; -HashMap VulkanContext::requested_device_extensions; - -void VulkanContext::register_requested_device_extension(const CharString &extension_name, bool p_required) { - ERR_FAIL_COND_MSG(device_extensions_initialized, "You can only registered extensions before the Vulkan instance is created"); - ERR_FAIL_COND(requested_device_extensions.has(extension_name)); - - requested_device_extensions[extension_name] = p_required; -} - -Error VulkanContext::_initialize_device_extensions() { - // Look for device extensions. - enabled_device_extension_names.clear(); - - // Make sure our core extensions are here - register_requested_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); - - register_requested_device_extension(VK_KHR_MULTIVIEW_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); - register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); - register_requested_device_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, false); - register_requested_device_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); - - if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { - register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); - } - - // TODO consider the following extensions: - // - VK_KHR_spirv_1_4 - // - VK_KHR_swapchain_mutable_format - // - VK_EXT_full_screen_exclusive - // - VK_EXT_hdr_metadata - // - VK_KHR_depth_stencil_resolve - - // Even though the user "enabled" the extension via the command - // line, we must make sure that it's enumerated for use with the - // device. Therefore, disable it here, and re-enable it again if - // enumerated. - if (VK_KHR_incremental_present_enabled) { - register_requested_device_extension(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, false); - } - if (VK_GOOGLE_display_timing_enabled) { - register_requested_device_extension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, false); - } - - // obtain available device extensions - uint32_t device_extension_count = 0; - VkResult err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - ERR_FAIL_COND_V_MSG(device_extension_count == 0, ERR_CANT_CREATE, - "vkEnumerateDeviceExtensionProperties failed to find any extensions\n\n" - "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" - "vkCreateInstance Failure"); - - VkExtensionProperties *device_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * device_extension_count); - err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, device_extensions); - if (err) { - free(device_extensions); - ERR_FAIL_V(ERR_CANT_CREATE); - } - -#ifdef DEV_ENABLED - for (uint32_t i = 0; i < device_extension_count; i++) { - print_verbose(String("VULKAN: Found device extension ") + String::utf8(device_extensions[i].extensionName)); - } -#endif - - // Enable all extensions that are supported and requested - for (uint32_t i = 0; i < device_extension_count; i++) { - CharString extension_name(device_extensions[i].extensionName); - if (requested_device_extensions.has(extension_name)) { - enabled_device_extension_names.insert(extension_name); - } - } - - // Now check our requested extensions - for (KeyValue &requested_extension : requested_device_extensions) { - if (!enabled_device_extension_names.has(requested_extension.key)) { - if (requested_extension.value) { - free(device_extensions); - ERR_FAIL_V_MSG(ERR_BUG, - String("vkEnumerateDeviceExtensionProperties failed to find the ") + String::utf8(requested_extension.key) + String(" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\nvkCreateInstance Failure")); - } else { - print_verbose(String("Optional extension ") + String::utf8(requested_extension.key) + String(" not found")); - } - } - } - - free(device_extensions); - - device_extensions_initialized = true; - return OK; -} - -uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const { - uint32_t flags = 0; - - if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { - flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT; - } - if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { - flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT; - } - if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { - flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT; - } - // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { - // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT; - // } - if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { - flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT; - } - if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { - flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT; - } - - return flags; -} - -String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { - String res; - - if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { - res += ", STAGE_VERTEX"; - } - if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { - res += ", STAGE_TESSELLATION_CONTROL"; - } - if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { - res += ", STAGE_TESSELLATION_EVALUATION"; - } - if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { - res += ", STAGE_GEOMETRY"; - } - if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { - res += ", STAGE_FRAGMENT"; - } - if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { - res += ", STAGE_COMPUTE"; - } - - // These are not defined on Android GRMBL. - if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { - res += ", STAGE_RAYGEN_KHR"; - } - if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { - res += ", STAGE_ANY_HIT_KHR"; - } - if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { - res += ", STAGE_CLOSEST_HIT_KHR"; - } - if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { - res += ", STAGE_MISS_KHR"; - } - if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { - res += ", STAGE_INTERSECTION_KHR"; - } - if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { - res += ", STAGE_CALLABLE_KHR"; - } - if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { - res += ", STAGE_TASK_NV"; - } - if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { - res += ", STAGE_MESH_NV"; - } - - return res.substr(2); // Remove first ", ". -} - -uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { - uint32_t flags = 0; - - if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { - flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT; - } - - return flags; -} - -String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { - String res; - - if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { - res += ", FEATURE_BASIC"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { - res += ", FEATURE_VOTE"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { - res += ", FEATURE_ARITHMETIC"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { - res += ", FEATURE_BALLOT"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { - res += ", FEATURE_SHUFFLE"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { - res += ", FEATURE_SHUFFLE_RELATIVE"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { - res += ", FEATURE_CLUSTERED"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { - res += ", FEATURE_QUAD"; - } - if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { - res += ", FEATURE_PARTITIONED_NV"; - } - - return res.substr(2); // Remove first ", ". -} - -Error VulkanContext::_check_capabilities() { - // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html - // https://www.khronos.org/blog/vulkan-subgroup-tutorial - - // For Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. - - // So we check if the functions are accessible by getting their function pointers and skipping if not - // (note that the desktop loader does a better job here but the android loader doesn't.) - - // Assume not supported until proven otherwise. - vrs_capabilities.pipeline_vrs_supported = false; - vrs_capabilities.primitive_vrs_supported = false; - vrs_capabilities.attachment_vrs_supported = false; - vrs_capabilities.min_texel_size = Size2i(); - vrs_capabilities.max_texel_size = Size2i(); - vrs_capabilities.texel_size = Size2i(); - 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; - subgroup_capabilities.size = 0; - subgroup_capabilities.min_size = 0; - subgroup_capabilities.max_size = 0; - subgroup_capabilities.supportedStages = 0; - subgroup_capabilities.supportedOperations = 0; - subgroup_capabilities.quadOperationsInAllStages = false; - subgroup_capabilities.size_control_is_supported = false; - shader_capabilities.shader_float16_is_supported = false; - shader_capabilities.shader_int8_is_supported = false; - storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = false; - storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = false; - storage_buffer_capabilities.storage_push_constant_16_is_supported = false; - storage_buffer_capabilities.storage_input_output_16 = false; - - if (is_instance_extension_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { - // Check for extended features. - PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); - if (vkGetPhysicalDeviceFeatures2_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name. - vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); - } - if (vkGetPhysicalDeviceFeatures2_func != nullptr) { - // Check our extended features. - void *next = nullptr; - - // We must check that the relative extension is present before assuming a - // feature as enabled. - // See also: https://github.com/godotengine/godot/issues/65409 - - VkPhysicalDeviceVulkan12Features device_features_vk12 = {}; - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {}; - VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; - VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; - VkPhysicalDeviceMultiviewFeatures multiview_features = {}; - VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {}; - - if (device_api_version >= VK_API_VERSION_1_2) { - device_features_vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - device_features_vk12.pNext = next; - next = &device_features_vk12; - } else { - if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { - shader_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, - /*pNext*/ next, - /*shaderFloat16*/ false, - /*shaderInt8*/ false, - }; - next = &shader_features; - } - } - - if (is_device_extension_enabled(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { - vrs_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, - /*pNext*/ next, - /*pipelineFragmentShadingRate*/ false, - /*primitiveFragmentShadingRate*/ false, - /*attachmentFragmentShadingRate*/ false, - }; - next = &vrs_features; - } - - if (is_device_extension_enabled(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { - storage_feature = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, - /*pNext*/ next, - /*storageBuffer16BitAccess*/ false, - /*uniformAndStorageBuffer16BitAccess*/ false, - /*storagePushConstant16*/ false, - /*storageInputOutput16*/ false, - }; - next = &storage_feature; - } - - if (is_device_extension_enabled(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { - multiview_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, - /*pNext*/ next, - /*multiview*/ false, - /*multiviewGeometryShader*/ false, - /*multiviewTessellationShader*/ false, - }; - next = &multiview_features; - } - - if (is_device_extension_enabled(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) { - pipeline_cache_control_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES, - /*pNext*/ next, - /*pipelineCreationCacheControl*/ false, - }; - next = &pipeline_cache_control_features; - } - - VkPhysicalDeviceFeatures2 device_features; - device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - device_features.pNext = next; - - vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); - - if (device_api_version >= VK_API_VERSION_1_2) { -#ifdef MACOS_ENABLED - ERR_FAIL_COND_V_MSG(!device_features_vk12.shaderSampledImageArrayNonUniformIndexing, ERR_CANT_CREATE, "Your GPU doesn't support shaderSampledImageArrayNonUniformIndexing which is required to use the Vulkan-based renderers in Godot."); -#endif - - if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { - shader_capabilities.shader_float16_is_supported = device_features_vk12.shaderFloat16; - shader_capabilities.shader_int8_is_supported = device_features_vk12.shaderInt8; - } - } else { - if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { - shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; - shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; - } - } - - if (is_device_extension_enabled(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { - vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; - vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; - vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; - } - - if (is_device_extension_enabled(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { - multiview_capabilities.is_supported = multiview_features.multiview; - multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; - multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; - } - - if (is_device_extension_enabled(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { - storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; - storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; - storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; - storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; - } - - if (is_device_extension_enabled(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) { - pipeline_cache_control_support = pipeline_cache_control_features.pipelineCreationCacheControl; - } - } - - // Check extended properties. - PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); - if (device_properties_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name. - device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); - } - if (device_properties_func != nullptr) { - VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties{}; - VkPhysicalDeviceMultiviewProperties multiviewProperties{}; - VkPhysicalDeviceSubgroupProperties subgroupProperties{}; - VkPhysicalDeviceSubgroupSizeControlProperties subgroupSizeControlProperties = {}; - VkPhysicalDeviceProperties2 physicalDeviceProperties{}; - void *nextptr = nullptr; - - if (device_api_version >= VK_API_VERSION_1_1) { // Vulkan 1.1 or higher - subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nextptr; - - nextptr = &subgroupProperties; - - subgroup_capabilities.size_control_is_supported = is_device_extension_enabled(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); - - if (subgroup_capabilities.size_control_is_supported) { - subgroupSizeControlProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; - subgroupSizeControlProperties.pNext = nextptr; - - nextptr = &subgroupSizeControlProperties; - } - } - - if (multiview_capabilities.is_supported) { - multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - multiviewProperties.pNext = nextptr; - - nextptr = &multiviewProperties; - } - - if (vrs_capabilities.attachment_vrs_supported) { - vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; - vrsProperties.pNext = nextptr; - - nextptr = &vrsProperties; - } - - physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - physicalDeviceProperties.pNext = nextptr; - - device_properties_func(gpu, &physicalDeviceProperties); - - subgroup_capabilities.size = subgroupProperties.subgroupSize; - subgroup_capabilities.min_size = subgroupProperties.subgroupSize; - subgroup_capabilities.max_size = subgroupProperties.subgroupSize; - subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; - subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; - // Note: quadOperationsInAllStages will be true if: - // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT. - // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT. - subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; - - if (subgroup_capabilities.size_control_is_supported && (subgroupSizeControlProperties.requiredSubgroupSizeStages & VK_SHADER_STAGE_COMPUTE_BIT)) { - subgroup_capabilities.min_size = subgroupSizeControlProperties.minSubgroupSize; - subgroup_capabilities.max_size = subgroupSizeControlProperties.maxSubgroupSize; - } - - if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { - print_verbose("- Vulkan Variable Rate Shading supported:"); - if (vrs_capabilities.pipeline_vrs_supported) { - print_verbose(" Pipeline fragment shading rate"); - } - if (vrs_capabilities.primitive_vrs_supported) { - print_verbose(" Primitive fragment shading rate"); - } - if (vrs_capabilities.attachment_vrs_supported) { - // TODO expose these somehow to the end user. - vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; - vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; - vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; - vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; - - // We'll attempt to default to a texel size of 16x16 - vrs_capabilities.texel_size.x = CLAMP(16, vrs_capabilities.min_texel_size.x, vrs_capabilities.max_texel_size.x); - vrs_capabilities.texel_size.y = CLAMP(16, vrs_capabilities.min_texel_size.y, vrs_capabilities.max_texel_size.y); - - print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); - } - - } else { - print_verbose("- Vulkan Variable Rate Shading not supported"); - } - - if (multiview_capabilities.is_supported) { - multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; - multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; - - print_verbose("- Vulkan multiview supported:"); - print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); - print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); - } else { - print_verbose("- Vulkan multiview not supported"); - } - - print_verbose("- Vulkan subgroup:"); - print_verbose(" size: " + itos(subgroup_capabilities.size)); - print_verbose(" min size: " + itos(subgroup_capabilities.min_size)); - print_verbose(" max size: " + itos(subgroup_capabilities.max_size)); - print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); - print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); - if (subgroup_capabilities.quadOperationsInAllStages) { - print_verbose(" quad operations in all stages"); - } - } else { - print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2"); - } - } - - return OK; -} - -Error VulkanContext::_create_instance() { - // Obtain Vulkan version. - _obtain_vulkan_version(); - - // Initialize extensions. - { - Error err = _initialize_instance_extensions(); - if (err != OK) { - return err; - } - } - - int enabled_extension_count = 0; - const char *enabled_extension_names[MAX_EXTENSIONS]; - ERR_FAIL_COND_V(enabled_instance_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); - for (const CharString &extension_name : enabled_instance_extension_names) { - enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); - } - - // We'll set application version to the Vulkan version we're developing against, even if our instance is based on - // an older Vulkan version, devices can still support newer versions of Vulkan. - // The exception is when we're on Vulkan 1.0, we should not set this to anything but 1.0. - // Note that this value is only used by validation layers to warn us about version issues. - uint32_t application_api_version = instance_api_version == VK_API_VERSION_1_0 ? VK_API_VERSION_1_0 : VK_API_VERSION_1_2; - - CharString cs = GLOBAL_GET("application/config/name").operator String().utf8(); - const VkApplicationInfo app = { - /*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO, - /*pNext*/ nullptr, - /*pApplicationName*/ cs.get_data(), - /*applicationVersion*/ 0, // It would be really nice if we store a version number in project settings, say "application/config/version" - /*pEngineName*/ VERSION_NAME, - /*engineVersion*/ VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), - /*apiVersion*/ application_api_version - }; - VkInstanceCreateInfo inst_info{}; - inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - inst_info.pApplicationInfo = &app; - inst_info.enabledExtensionCount = enabled_extension_count; - inst_info.ppEnabledExtensionNames = (const char *const *)enabled_extension_names; - if (_use_validation_layers()) { - _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames); - } - - /* - * This is info for a temp callback to use during CreateInstance. - * After the instance is created, we use the instance-based - * function to register the final callback. - */ - VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info = {}; - VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info = {}; - if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - // VK_EXT_debug_utils style. - dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - dbg_messenger_create_info.pNext = nullptr; - dbg_messenger_create_info.flags = 0; - dbg_messenger_create_info.messageSeverity = - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback; - dbg_messenger_create_info.pUserData = this; - inst_info.pNext = &dbg_messenger_create_info; - } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { - dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | - VK_DEBUG_REPORT_WARNING_BIT_EXT | - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | - VK_DEBUG_REPORT_ERROR_BIT_EXT | - VK_DEBUG_REPORT_DEBUG_BIT_EXT; - dbg_report_callback_create_info.pfnCallback = _debug_report_callback; - dbg_report_callback_create_info.pUserData = this; - inst_info.pNext = &dbg_report_callback_create_info; - } - - VkResult err; - - if (vulkan_hooks) { - if (!vulkan_hooks->create_vulkan_instance(&inst_info, &inst)) { - return ERR_CANT_CREATE; - } - } else { - err = vkCreateInstance(&inst_info, nullptr, &inst); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, - "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, - "Cannot find a specified extension library.\n" - "Make sure your layers path is set appropriately.\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, - "vkCreateInstance failed.\n\n" - "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" - "Please look at the Getting Started guide for additional information.\n" - "vkCreateInstance Failure"); - } - - inst_initialized = true; - -#ifdef USE_VOLK - volkLoadInstance(inst); -#endif - - if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). - CreateDebugUtilsMessengerEXT = - (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); - DestroyDebugUtilsMessengerEXT = - (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT"); - SubmitDebugUtilsMessageEXT = - (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT"); - CmdBeginDebugUtilsLabelEXT = - (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT"); - CmdEndDebugUtilsLabelEXT = - (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT"); - CmdInsertDebugUtilsLabelEXT = - (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT"); - SetDebugUtilsObjectNameEXT = - (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT"); - if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT || - nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT || - nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT || - nullptr == SetDebugUtilsObjectNameEXT) { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "GetProcAddr: Failed to init VK_EXT_debug_utils\n" - "GetProcAddr: Failure"); - } - - err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger); - switch (err) { - case VK_SUCCESS: - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugUtilsMessengerEXT: out of host memory\n" - "CreateDebugUtilsMessengerEXT Failure"); - break; - default: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugUtilsMessengerEXT: unknown failure\n" - "CreateDebugUtilsMessengerEXT Failure"); - ERR_FAIL_V(ERR_CANT_CREATE); - break; - } - } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { - CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT"); - DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT"); - DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT"); - - if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "GetProcAddr: Failed to init VK_EXT_debug_report\n" - "GetProcAddr: Failure"); - } - - err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report); - switch (err) { - case VK_SUCCESS: - break; - case VK_ERROR_OUT_OF_HOST_MEMORY: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugReportCallbackEXT: out of host memory\n" - "CreateDebugReportCallbackEXT Failure"); - break; - default: - ERR_FAIL_V_MSG(ERR_CANT_CREATE, - "CreateDebugReportCallbackEXT: unknown failure\n" - "CreateDebugReportCallbackEXT Failure"); - ERR_FAIL_V(ERR_CANT_CREATE); - break; - } - } - - return OK; -} - -Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { - // Make initial call to query gpu_count, then second call for gpu info. - uint32_t gpu_count = 0; - VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE, - "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n" - "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" - "vkEnumeratePhysicalDevices Failure"); - - VkPhysicalDevice *physical_devices = (VkPhysicalDevice *)malloc(sizeof(VkPhysicalDevice) * gpu_count); - err = vkEnumeratePhysicalDevices(inst, &gpu_count, physical_devices); - if (err) { - free(physical_devices); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - static const struct { - uint32_t id; - const char *name; - } vendor_names[] = { - { 0x1002, "AMD" }, - { 0x1010, "ImgTec" }, - { 0x106B, "Apple" }, - { 0x10DE, "NVIDIA" }, - { 0x13B5, "ARM" }, - { 0x5143, "Qualcomm" }, - { 0x8086, "Intel" }, - { 0, nullptr }, - }; - - int32_t device_index = -1; - if (vulkan_hooks) { - if (!vulkan_hooks->get_physical_device(&gpu)) { - return ERR_CANT_CREATE; - } - - // Not really needed but nice to print the correct entry. - for (uint32_t i = 0; i < gpu_count; ++i) { - if (physical_devices[i] == gpu) { - device_index = i; - break; - } - } - } else { - // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. - // The device should really be a preference, but for now choosing a discrete GPU over the - // integrated one is better than the default. - - int type_selected = -1; - print_verbose("Vulkan devices:"); - for (uint32_t i = 0; i < gpu_count; ++i) { - VkPhysicalDeviceProperties props; - vkGetPhysicalDeviceProperties(physical_devices[i], &props); - - bool present_supported = false; - - if (p_surface) { - uint32_t device_queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); - VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); - for (uint32_t j = 0; j < device_queue_family_count; j++) { - if ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { - VkBool32 supports; - err = vkGetPhysicalDeviceSurfaceSupportKHR( - physical_devices[i], j, p_surface, &supports); - if (err == VK_SUCCESS && supports) { - present_supported = true; - } else { - continue; - } - } - } - free(device_queue_props); - } - String name = String::utf8(props.deviceName); - String vendor = "Unknown"; - String dev_type; - switch (props.deviceType) { - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { - dev_type = "Discrete"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { - dev_type = "Integrated"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { - dev_type = "Virtual"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { - dev_type = "CPU"; - } break; - default: { - dev_type = "Other"; - } break; - } - uint32_t vendor_idx = 0; - while (vendor_names[vendor_idx].name != nullptr) { - if (props.vendorID == vendor_names[vendor_idx].id) { - vendor = vendor_names[vendor_idx].name; - break; - } - vendor_idx++; - } - print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); - - if (present_supported || !p_surface) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. - switch (props.deviceType) { - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { - if (type_selected < 4) { - type_selected = 4; - device_index = i; - } - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { - if (type_selected < 3) { - type_selected = 3; - device_index = i; - } - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { - if (type_selected < 2) { - type_selected = 2; - device_index = i; - } - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { - if (type_selected < 1) { - type_selected = 1; - device_index = i; - } - } break; - default: { - if (type_selected < 0) { - type_selected = 0; - device_index = i; - } - } break; - } - } - } - - int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. - if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) { - device_index = user_device_index; - } - - ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues."); - - gpu = physical_devices[device_index]; - } - - free(physical_devices); - - // Get identifier properties. - vkGetPhysicalDeviceProperties(gpu, &gpu_props); - - device_name = String::utf8(gpu_props.deviceName); - device_type = gpu_props.deviceType; - pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE); - pipeline_cache_id += "-driver-" + itos(gpu_props.driverVersion); - { - device_vendor = "Unknown"; - uint32_t vendor_idx = 0; - while (vendor_names[vendor_idx].name != nullptr) { - if (gpu_props.vendorID == vendor_names[vendor_idx].id) { - device_vendor = vendor_names[vendor_idx].name; - break; - } - vendor_idx++; - } - } - - // Get device version - device_api_version = gpu_props.apiVersion; - - String rendering_method; - if (OS::get_singleton()->get_current_rendering_method() == "mobile") { - rendering_method = "Forward Mobile"; - } else { - rendering_method = "Forward+"; - } - - // Output our device version - print_line(vformat("Vulkan API %s - %s - Using Vulkan Device #%d: %s - %s", get_device_api_version(), rendering_method, device_index, device_vendor, device_name)); - - { - Error _err = _initialize_device_extensions(); - if (_err != OK) { - return _err; - } - } - - // Call with nullptr data to get count. - vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); - ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); - - queue_props = (VkQueueFamilyProperties *)malloc(queue_family_count * sizeof(VkQueueFamilyProperties)); - vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, queue_props); - // Query fine-grained feature support for this device. - // If app has specific feature requirements it should check supported - // features based on this query - VkPhysicalDeviceFeatures features = {}; - vkGetPhysicalDeviceFeatures(gpu, &features); - - // Check required features and abort if any of them is missing. - if (!features.imageCubeArray || !features.independentBlend) { - String error_string = vformat("Your GPU (%s) does not support the following features which are required to use Vulkan-based renderers in Godot:\n\n", device_name); - if (!features.imageCubeArray) { - error_string += "- No support for image cube arrays.\n"; - } - if (!features.independentBlend) { - error_string += "- No support for independentBlend.\n"; - } - error_string += "\nThis is usually a hardware limitation, so updating graphics drivers won't help in most cases."; - -#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) - // Android/iOS platform ports currently don't exit themselves when this method returns `ERR_CANT_CREATE`. - OS::get_singleton()->alert(error_string + "\nClick OK to exit (black screen will be visible)."); -#else - OS::get_singleton()->alert(error_string + "\nClick OK to exit."); -#endif - - return ERR_CANT_CREATE; - } - - memset(&physical_device_features, 0, sizeof(physical_device_features)); -#define VK_DEVICEFEATURE_ENABLE_IF(x) \ - if (features.x) { \ - physical_device_features.x = features.x; \ - } else \ - ((void)0) - - // - // Opt-in to the features we actually need/use. These can be changed in the future. - // We do this for multiple reasons: - // - // 1. Certain features (like sparse* stuff) cause unnecessary internal driver allocations. - // 2. Others like shaderStorageImageMultisample are a huge red flag - // (MSAA + Storage is rarely needed). - // 3. Most features when turned off aren't actually off (we just promise the driver not to use them) - // and it is validation what will complain. This allows us to target a minimum baseline. - // - // TODO: Allow the user to override these settings (i.e. turn off more stuff) using profiles - // so they can target a broad range of HW. For example Mali HW does not have - // shaderClipDistance/shaderCullDistance; thus validation would complain if such feature is used; - // allowing them to fix the problem without even owning Mali HW to test on. - // - - // Turn off robust buffer access, which can hamper performance on some hardware. - //VK_DEVICEFEATURE_ENABLE_IF(robustBufferAccess); - VK_DEVICEFEATURE_ENABLE_IF(fullDrawIndexUint32); - VK_DEVICEFEATURE_ENABLE_IF(imageCubeArray); - VK_DEVICEFEATURE_ENABLE_IF(independentBlend); - VK_DEVICEFEATURE_ENABLE_IF(geometryShader); - VK_DEVICEFEATURE_ENABLE_IF(tessellationShader); - VK_DEVICEFEATURE_ENABLE_IF(sampleRateShading); - VK_DEVICEFEATURE_ENABLE_IF(dualSrcBlend); - VK_DEVICEFEATURE_ENABLE_IF(logicOp); - VK_DEVICEFEATURE_ENABLE_IF(multiDrawIndirect); - VK_DEVICEFEATURE_ENABLE_IF(drawIndirectFirstInstance); - VK_DEVICEFEATURE_ENABLE_IF(depthClamp); - VK_DEVICEFEATURE_ENABLE_IF(depthBiasClamp); - VK_DEVICEFEATURE_ENABLE_IF(fillModeNonSolid); - VK_DEVICEFEATURE_ENABLE_IF(depthBounds); - VK_DEVICEFEATURE_ENABLE_IF(wideLines); - VK_DEVICEFEATURE_ENABLE_IF(largePoints); - VK_DEVICEFEATURE_ENABLE_IF(alphaToOne); - VK_DEVICEFEATURE_ENABLE_IF(multiViewport); - VK_DEVICEFEATURE_ENABLE_IF(samplerAnisotropy); - VK_DEVICEFEATURE_ENABLE_IF(textureCompressionETC2); - VK_DEVICEFEATURE_ENABLE_IF(textureCompressionASTC_LDR); - VK_DEVICEFEATURE_ENABLE_IF(textureCompressionBC); - //VK_DEVICEFEATURE_ENABLE_IF(occlusionQueryPrecise); - //VK_DEVICEFEATURE_ENABLE_IF(pipelineStatisticsQuery); - VK_DEVICEFEATURE_ENABLE_IF(vertexPipelineStoresAndAtomics); - VK_DEVICEFEATURE_ENABLE_IF(fragmentStoresAndAtomics); - VK_DEVICEFEATURE_ENABLE_IF(shaderTessellationAndGeometryPointSize); - VK_DEVICEFEATURE_ENABLE_IF(shaderImageGatherExtended); - VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageExtendedFormats); - // Intel Arc doesn't support shaderStorageImageMultisample (yet? could be a driver thing), so it's - // better for Validation to scream at us if we use it. Furthermore MSAA Storage is a huge red flag - // for performance. - //VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageMultisample); - VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageReadWithoutFormat); - VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageWriteWithoutFormat); - VK_DEVICEFEATURE_ENABLE_IF(shaderUniformBufferArrayDynamicIndexing); - VK_DEVICEFEATURE_ENABLE_IF(shaderSampledImageArrayDynamicIndexing); - VK_DEVICEFEATURE_ENABLE_IF(shaderStorageBufferArrayDynamicIndexing); - VK_DEVICEFEATURE_ENABLE_IF(shaderStorageImageArrayDynamicIndexing); - VK_DEVICEFEATURE_ENABLE_IF(shaderClipDistance); - VK_DEVICEFEATURE_ENABLE_IF(shaderCullDistance); - VK_DEVICEFEATURE_ENABLE_IF(shaderFloat64); - VK_DEVICEFEATURE_ENABLE_IF(shaderInt64); - VK_DEVICEFEATURE_ENABLE_IF(shaderInt16); - //VK_DEVICEFEATURE_ENABLE_IF(shaderResourceResidency); - VK_DEVICEFEATURE_ENABLE_IF(shaderResourceMinLod); - // We don't use sparse features and enabling them cause extra internal - // allocations inside the Vulkan driver we don't need. - //VK_DEVICEFEATURE_ENABLE_IF(sparseBinding); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidencyBuffer); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidencyImage2D); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidencyImage3D); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidency2Samples); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidency4Samples); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidency8Samples); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidency16Samples); - //VK_DEVICEFEATURE_ENABLE_IF(sparseResidencyAliased); - VK_DEVICEFEATURE_ENABLE_IF(variableMultisampleRate); - //VK_DEVICEFEATURE_ENABLE_IF(inheritedQueries); - -#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ - { \ - fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ - ERR_FAIL_NULL_V_MSG(fp##entrypoint, ERR_CANT_CREATE, \ - "vkGetInstanceProcAddr failed to find vk" #entrypoint); \ - } - - GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceSupportKHR); - GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceFormatsKHR); - GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); - GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); - - // Gets capability info for current Vulkan driver. - { - Error res = _check_capabilities(); - if (res != OK) { - return res; - } - } - - device_initialized = true; - return OK; -} - -Error VulkanContext::_create_device(VkDevice &r_vk_device) { - VkResult err; - float queue_priorities[1] = { 0.0 }; - VkDeviceQueueCreateInfo queues[2]; - queues[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queues[0].pNext = nullptr; - queues[0].queueFamilyIndex = graphics_queue_family_index; - queues[0].queueCount = 1; - queues[0].pQueuePriorities = queue_priorities; - queues[0].flags = 0; - - // Before we retrieved what is supported, here we tell Vulkan we want to enable these features using the same structs. - void *nextptr = nullptr; - - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { - /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, - /*pNext*/ nextptr, - /*shaderFloat16*/ shader_capabilities.shader_float16_is_supported, - /*shaderInt8*/ shader_capabilities.shader_int8_is_supported, - }; - nextptr = &shader_features; - - VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; - if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { - // Insert into our chain to enable these features if they are available. - vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; - vrs_features.pNext = nextptr; - vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; - vrs_features.primitiveFragmentShadingRate = vrs_capabilities.primitive_vrs_supported; - vrs_features.attachmentFragmentShadingRate = vrs_capabilities.attachment_vrs_supported; - - nextptr = &vrs_features; - } - - VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {}; - if (pipeline_cache_control_support) { - pipeline_cache_control_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES; - pipeline_cache_control_features.pNext = nextptr; - pipeline_cache_control_features.pipelineCreationCacheControl = pipeline_cache_control_support; - - nextptr = &pipeline_cache_control_features; - } - - VkPhysicalDeviceVulkan11Features vulkan11features = {}; - VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; - VkPhysicalDeviceMultiviewFeatures multiview_features = {}; - if (device_api_version >= VK_API_VERSION_1_2) { - // In Vulkan 1.2 and newer we use a newer struct to enable various features. - - vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; - vulkan11features.pNext = nextptr; - vulkan11features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; - vulkan11features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; - vulkan11features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; - vulkan11features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; - vulkan11features.multiview = multiview_capabilities.is_supported; - vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; - vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; - vulkan11features.variablePointersStorageBuffer = 0; - vulkan11features.variablePointers = 0; - vulkan11features.protectedMemory = 0; - vulkan11features.samplerYcbcrConversion = 0; - vulkan11features.shaderDrawParameters = 0; - nextptr = &vulkan11features; - } else { - // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. - storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; - storage_feature.pNext = nextptr; - storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; - storage_feature.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; - storage_feature.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; - storage_feature.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; - nextptr = &storage_feature; - - if (device_api_version >= VK_API_VERSION_1_1) { // any Vulkan 1.1.x version - multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - multiview_features.pNext = nextptr; - multiview_features.multiview = multiview_capabilities.is_supported; - multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; - multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; - nextptr = &multiview_features; - } - } - - uint32_t enabled_extension_count = 0; - const char *enabled_extension_names[MAX_EXTENSIONS]; - ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); - for (const CharString &extension_name : enabled_device_extension_names) { - enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); - } - - VkDeviceCreateInfo sdevice = { - /*sType*/ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - /*pNext*/ nextptr, - /*flags*/ 0, - /*queueCreateInfoCount*/ 1, - /*pQueueCreateInfos*/ queues, - /*enabledLayerCount*/ 0, - /*ppEnabledLayerNames*/ nullptr, - /*enabledExtensionCount*/ enabled_extension_count, - /*ppEnabledExtensionNames*/ (const char *const *)enabled_extension_names, - /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here. - }; - if (separate_present_queue) { - queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queues[1].pNext = nullptr; - queues[1].queueFamilyIndex = present_queue_family_index; - queues[1].queueCount = 1; - queues[1].pQueuePriorities = queue_priorities; - queues[1].flags = 0; - sdevice.queueCreateInfoCount = 2; - } - - if (vulkan_hooks) { - if (!vulkan_hooks->create_vulkan_device(&sdevice, &r_vk_device)) { - return ERR_CANT_CREATE; - } - } else { - err = vkCreateDevice(gpu, &sdevice, nullptr, &r_vk_device); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - } - - return OK; -} - -Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { - // Iterate over each queue to learn whether it supports presenting: - VkBool32 *supportsPresent = nullptr; - - if (p_surface) { - supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32)); - for (uint32_t i = 0; i < queue_family_count; i++) { - fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]); - } - } - - // Search for a graphics and a present queue in the array of queue - // families, try to find one that supports both. - uint32_t graphicsQueueFamilyIndex = UINT32_MAX; - uint32_t presentQueueFamilyIndex = UINT32_MAX; - for (uint32_t i = 0; i < queue_family_count; i++) { - if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { - if (graphicsQueueFamilyIndex == UINT32_MAX) { - graphicsQueueFamilyIndex = i; - } - - if (p_surface && supportsPresent[i] == VK_TRUE) { - graphicsQueueFamilyIndex = i; - presentQueueFamilyIndex = i; - break; - } - } - } - - if (p_surface && presentQueueFamilyIndex == UINT32_MAX) { - // If didn't find a queue that supports both graphics and present, then - // find a separate present queue. - for (uint32_t i = 0; i < queue_family_count; ++i) { - if (supportsPresent[i] == VK_TRUE) { - presentQueueFamilyIndex = i; - break; - } - } - } - - if (p_surface) { - free(supportsPresent); - - // Generate error if could not find both a graphics and a present queue. - ERR_FAIL_COND_V_MSG(graphicsQueueFamilyIndex == UINT32_MAX || presentQueueFamilyIndex == UINT32_MAX, ERR_CANT_CREATE, - "Could not find both graphics and present queues\n"); - - graphics_queue_family_index = graphicsQueueFamilyIndex; - present_queue_family_index = presentQueueFamilyIndex; - separate_present_queue = (graphics_queue_family_index != present_queue_family_index); - } else { - graphics_queue_family_index = graphicsQueueFamilyIndex; - } - - _create_device(device); - driver = memnew(RenderingDeviceDriverVulkan(this, device)); - - static PFN_vkGetDeviceProcAddr g_gdpa = nullptr; -#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ - { \ - if (!g_gdpa) \ - g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(inst, "vkGetDeviceProcAddr"); \ - fp##entrypoint = (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint); \ - ERR_FAIL_NULL_V_MSG(fp##entrypoint, ERR_CANT_CREATE, \ - "vkGetDeviceProcAddr failed to find vk" #entrypoint); \ - } - - GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR); - GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR); - GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR); - GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR); - GET_DEVICE_PROC_ADDR(device, QueuePresentKHR); - if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { - GET_DEVICE_PROC_ADDR(device, GetRefreshCycleDurationGOOGLE); - GET_DEVICE_PROC_ADDR(device, GetPastPresentationTimingGOOGLE); - } - - vkGetDeviceQueue(device, graphics_queue_family_index, 0, &graphics_queue); - - if (p_surface) { - if (!separate_present_queue) { - present_queue = graphics_queue; - } else { - vkGetDeviceQueue(device, present_queue_family_index, 0, &present_queue); - } - - // Get the list of VkFormat's that are supported: - uint32_t formatCount; - VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); - err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats); - if (err) { - free(surfFormats); - ERR_FAIL_V(ERR_CANT_CREATE); - } - // If the format list includes just one entry of VK_FORMAT_UNDEFINED, - // the surface has no preferred format. Otherwise, at least one - // supported format will be returned. - if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { - format = VK_FORMAT_B8G8R8A8_UNORM; - color_space = surfFormats[0].colorSpace; - } else { - // These should be ordered with the ones we want to use on top and fallback modes further down - // we want a 32bit RGBA unsigned normalized buffer or similar. - const VkFormat allowed_formats[] = { - VK_FORMAT_B8G8R8A8_UNORM, - VK_FORMAT_R8G8B8A8_UNORM - }; - uint32_t allowed_formats_count = sizeof(allowed_formats) / sizeof(VkFormat); - - if (formatCount < 1) { - free(surfFormats); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1"); - } - - // Find the first format that we support. - format = VK_FORMAT_UNDEFINED; - for (uint32_t af = 0; af < allowed_formats_count && format == VK_FORMAT_UNDEFINED; af++) { - for (uint32_t sf = 0; sf < formatCount && format == VK_FORMAT_UNDEFINED; sf++) { - if (surfFormats[sf].format == allowed_formats[af]) { - format = surfFormats[sf].format; - color_space = surfFormats[sf].colorSpace; - } - } - } - - if (format == VK_FORMAT_UNDEFINED) { - free(surfFormats); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found."); - } - } - - free(surfFormats); - } - - Error serr = _create_semaphores(); - if (serr) { - return serr; - } - - queues_initialized = true; - return OK; -} - -Error VulkanContext::_create_semaphores() { - VkResult err; - - // Create semaphores to synchronize acquiring presentable buffers before - // rendering and waiting for drawing to be complete before presenting. - VkSemaphoreCreateInfo semaphoreCreateInfo = { - /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - }; - - // Create fences that we can use to throttle if we get too far - // ahead of the image presents. - VkFenceCreateInfo fence_ci = { - /*sType*/ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ VK_FENCE_CREATE_SIGNALED_BIT - }; - for (uint32_t i = 0; i < FRAME_LAG; i++) { - err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - if (separate_present_queue) { - err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_ownership_semaphores[i]); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - } - } - frame_index = 0; - - // Get Memory information and properties. - vkGetPhysicalDeviceMemoryProperties(gpu, &memory_properties); - - return OK; -} - -bool VulkanContext::_use_validation_layers() { - return Engine::get_singleton()->is_validation_layers_enabled(); -} - -VkExtent2D VulkanContext::_compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const { - // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. - if (p_surf_capabilities.currentExtent.width == 0xFFFFFFFF) { - // If the surface size is undefined, the size is set to the size - // of the images requested, which must fit within the minimum and - // maximum values. - VkExtent2D extent = {}; - extent.width = CLAMP((uint32_t)(*p_window_width), p_surf_capabilities.minImageExtent.width, p_surf_capabilities.maxImageExtent.width); - extent.height = CLAMP((uint32_t)(*p_window_height), p_surf_capabilities.minImageExtent.height, p_surf_capabilities.maxImageExtent.height); - return extent; - } else { - // If the surface size is defined, the swap chain size must match. - *p_window_width = p_surf_capabilities.currentExtent.width; - *p_window_height = p_surf_capabilities.currentExtent.height; - return p_surf_capabilities.currentExtent; - } -} - -Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { - ERR_FAIL_NULL_V_MSG(_get_platform_surface_extension(), ERR_UNAVAILABLE, "This Vulkan context is headless."); - - ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); - - if (!device_initialized) { - Error err = _create_physical_device(p_surface); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - } - - if (!queues_initialized) { - // We use a single GPU, but we need a surface to initialize the - // queues, so this process must be deferred until a surface - // is created. - Error err = _initialize_queues(p_surface); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - } - - Window window; - window.surface = p_surface; - 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 VulkanContext::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 VulkanContext::window_get_width(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), -1); - return windows[p_window].width; -} - -int VulkanContext::window_get_height(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), -1); - return windows[p_window].height; -} - -bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), false); - Window *w = &windows[p_window]; - return w->swapchain_image_resources != VK_NULL_HANDLE; -} - -RDD::RenderPassID VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), RDD::RenderPassID()); - Window *w = &windows[p_window]; - return (RDD::RenderPassID)w->render_pass; -} - -RDD::FramebufferID VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_window) { - ERR_FAIL_COND_V(!windows.has(p_window), RDD::FramebufferID()); - ERR_FAIL_COND_V(!buffers_prepared, RDD::FramebufferID()); - Window *w = &windows[p_window]; - if (w->swapchain_image_resources != VK_NULL_HANDLE) { - return (RDD::FramebufferID)w->swapchain_image_resources[w->current_buffer].framebuffer; - } else { - return RDD::FramebufferID(); - } -} - -void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { - ERR_FAIL_COND(!windows.has(p_window_id)); - _clean_up_swap_chain(&windows[p_window_id]); - - vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); - windows.erase(p_window_id); -} - -Error VulkanContext::_clean_up_swap_chain(Window *window) { - if (!window->swapchain) { - return OK; - } - vkDeviceWaitIdle(device); - - // This destroys images associated it seems. - fpDestroySwapchainKHR(device, window->swapchain, nullptr); - window->swapchain = VK_NULL_HANDLE; - vkDestroyRenderPass(device, window->render_pass, nullptr); - window->render_pass = VK_NULL_HANDLE; - if (window->swapchain_image_resources) { - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device, window->swapchain_image_resources[i].view, nullptr); - vkDestroyFramebuffer(device, window->swapchain_image_resources[i].framebuffer, nullptr); - } - - free(window->swapchain_image_resources); - window->swapchain_image_resources = nullptr; - swapchainImageCount = 0; - } - if (separate_present_queue) { - vkDestroyCommandPool(device, window->present_cmd_pool, nullptr); - } - - for (uint32_t i = 0; i < FRAME_LAG; i++) { - // Destroy the semaphores now (we'll re-create it later if we have to). - // We must do this because the semaphore cannot be reused if it's in a signaled state - // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR) - // The only way to reset it would be to present the swapchain... the one we just destroyed. - // And the API has no way to "unsignal" the semaphore. - vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr); - window->image_acquired_semaphores[i] = 0; - } - - return OK; -} - -Error VulkanContext::_update_swap_chain(Window *window) { - VkResult err; - - if (window->swapchain) { - _clean_up_swap_chain(window); - } - - // Check the surface capabilities and formats. - VkSurfaceCapabilitiesKHR surfCapabilities; - err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - { - VkBool32 supports = VK_FALSE; - err = vkGetPhysicalDeviceSurfaceSupportKHR( - gpu, present_queue_family_index, window->surface, &supports); - ERR_FAIL_COND_V_MSG(err != VK_SUCCESS || supports == false, ERR_CANT_CREATE, - "Window's surface is not supported by device. Did the GPU go offline? Was the window " - "created on another monitor? Check previous errors & try launching with " - "--gpu-validation."); - } - - uint32_t presentModeCount; - err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - VkPresentModeKHR *presentModes = (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); - ERR_FAIL_NULL_V(presentModes, ERR_CANT_CREATE); - err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, presentModes); - if (err) { - free(presentModes); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - VkExtent2D swapchainExtent = _compute_swapchain_extent(surfCapabilities, &window->width, &window->height); - - if (window->width == 0 || window->height == 0) { - free(presentModes); - // Likely window minimized, no swapchain created. - return ERR_SKIP; - } - // The FIFO present mode is guaranteed by the spec to be supported - // and to have no tearing. It's a great default present mode to use. - - // There are times when you may wish to use another present mode. The - // following code shows how to select them, and the comments provide some - // reasons you may wish to use them. - // - // It should be noted that Vulkan 1.0 doesn't provide a method for - // synchronizing rendering with the presentation engine's display. There - // is a method provided for throttling rendering with the display, but - // there are some presentation engines for which this method will not work. - // If an application doesn't throttle its rendering, and if it renders much - // faster than the refresh rate of the display, this can waste power on - // mobile devices. That is because power is being spent rendering images - // that may never be seen. - - // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about - // tearing, or have some way of synchronizing their rendering with the - // display. - // VK_PRESENT_MODE_MAILBOX_KHR may be useful for applications that - // generally render a new presentable image every refresh cycle, but are - // occasionally early. In this case, the application wants the new image - // to be displayed instead of the previously-queued-for-presentation image - // that has not yet been displayed. - // VK_PRESENT_MODE_FIFO_RELAXED_KHR is for applications that generally - // render a new presentable image every refresh cycle, but are occasionally - // late. In this case (perhaps because of stuttering/latency concerns), - // the application wants the late image to be immediately displayed, even - // though that may mean some tearing. - - VkPresentModeKHR requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; - switch (window->vsync_mode) { - case DisplayServer::VSYNC_MAILBOX: - requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR; - break; - case DisplayServer::VSYNC_ADAPTIVE: - requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR; - break; - case DisplayServer::VSYNC_ENABLED: - requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; - break; - case DisplayServer::VSYNC_DISABLED: - requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR; - break; - } - - // Check if the requested mode is available. - bool present_mode_available = false; - for (uint32_t i = 0; i < presentModeCount; i++) { - if (presentModes[i] == requested_present_mode) { - present_mode_available = true; - } - } - - // Set the windows present mode if it is available, otherwise FIFO is used (guaranteed supported). - if (present_mode_available) { - if (window->presentMode != requested_present_mode) { - window->presentMode = requested_present_mode; - print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); - } - } 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. - } - - free(presentModes); - - // Determine the number of VkImages to use in the swap chain. - // Application desires to acquire 3 images at a time for triple - // buffering. - uint32_t desiredNumOfSwapchainImages = 3; - if (desiredNumOfSwapchainImages < surfCapabilities.minImageCount) { - desiredNumOfSwapchainImages = surfCapabilities.minImageCount; - } - // If maxImageCount is 0, we can ask for as many images as we want; - // otherwise we're limited to maxImageCount. - if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { - // Application must settle for fewer images than desired. - desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; - } - - VkSurfaceTransformFlagsKHR preTransform; - if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { - preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - } else { - preTransform = surfCapabilities.currentTransform; - } - - VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - - if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { - // Find a supported composite alpha mode - one of these is guaranteed to be set. - VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - }; - - for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { - if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { - compositeAlpha = compositeAlphaFlags[i]; - break; - } - } - } - - VkSwapchainCreateInfoKHR swapchain_ci = { - /*sType*/ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - /*pNext*/ nullptr, - /*flags*/ 0, - /*surface*/ window->surface, - /*minImageCount*/ desiredNumOfSwapchainImages, - /*imageFormat*/ format, - /*imageColorSpace*/ color_space, - /*imageExtent*/ { - /*width*/ swapchainExtent.width, - /*height*/ swapchainExtent.height, - }, - /*imageArrayLayers*/ 1, - /*imageUsage*/ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - /*imageSharingMode*/ VK_SHARING_MODE_EXCLUSIVE, - /*queueFamilyIndexCount*/ 0, - /*pQueueFamilyIndices*/ nullptr, - /*preTransform*/ (VkSurfaceTransformFlagBitsKHR)preTransform, - /*compositeAlpha*/ compositeAlpha, - /*presentMode*/ window->presentMode, - /*clipped*/ true, - /*oldSwapchain*/ VK_NULL_HANDLE, - }; - - err = fpCreateSwapchainKHR(device, &swapchain_ci, nullptr, &window->swapchain); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - uint32_t sp_image_count; - err = fpGetSwapchainImagesKHR(device, window->swapchain, &sp_image_count, nullptr); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - if (swapchainImageCount == 0) { - // Assign here for the first time. - swapchainImageCount = sp_image_count; - } else { - ERR_FAIL_COND_V(swapchainImageCount != sp_image_count, ERR_BUG); - } - - VkImage *swapchainImages = (VkImage *)malloc(swapchainImageCount * sizeof(VkImage)); - ERR_FAIL_NULL_V(swapchainImages, ERR_CANT_CREATE); - err = fpGetSwapchainImagesKHR(device, window->swapchain, &swapchainImageCount, swapchainImages); - if (err) { - free(swapchainImages); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - window->swapchain_image_resources = - (SwapchainImageResources *)malloc(sizeof(SwapchainImageResources) * swapchainImageCount); - if (!window->swapchain_image_resources) { - free(swapchainImages); - ERR_FAIL_V(ERR_CANT_CREATE); - } - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - VkImageViewCreateInfo color_image_view = { - /*sType*/ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - /*image*/ swapchainImages[i], - /*viewType*/ VK_IMAGE_VIEW_TYPE_2D, - /*format*/ format, - /*components*/ { - /*r*/ VK_COMPONENT_SWIZZLE_R, - /*g*/ VK_COMPONENT_SWIZZLE_G, - /*b*/ VK_COMPONENT_SWIZZLE_B, - /*a*/ VK_COMPONENT_SWIZZLE_A, - }, - /*subresourceRange*/ { /*aspectMask*/ VK_IMAGE_ASPECT_COLOR_BIT, - /*baseMipLevel*/ 0, - /*levelCount*/ 1, - /*baseArrayLayer*/ 0, - /*layerCount*/ 1 }, - }; - - window->swapchain_image_resources[i].image = swapchainImages[i]; - - color_image_view.image = window->swapchain_image_resources[i].image; - - err = vkCreateImageView(device, &color_image_view, nullptr, &window->swapchain_image_resources[i].view); - if (err) { - free(swapchainImages); - ERR_FAIL_V(ERR_CANT_CREATE); - } - } - - free(swapchainImages); - - /******** FRAMEBUFFER ************/ - - { - const VkAttachmentDescription2KHR attachment = { - /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, - /*pNext*/ nullptr, - /*flags*/ 0, - /*format*/ format, - /*samples*/ VK_SAMPLE_COUNT_1_BIT, - /*loadOp*/ VK_ATTACHMENT_LOAD_OP_CLEAR, - /*storeOp*/ VK_ATTACHMENT_STORE_OP_STORE, - /*stencilLoadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, - /*stencilStoreOp*/ VK_ATTACHMENT_STORE_OP_DONT_CARE, - /*initialLayout*/ VK_IMAGE_LAYOUT_UNDEFINED, - /*finalLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - - }; - const VkAttachmentReference2KHR color_reference = { - /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, - /*pNext*/ nullptr, - /*attachment*/ 0, - /*layout*/ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - /*aspectMask*/ 0, - }; - - const VkSubpassDescription2KHR subpass = { - /*sType*/ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, - /*pNext*/ nullptr, - /*flags*/ 0, - /*pipelineBindPoint*/ VK_PIPELINE_BIND_POINT_GRAPHICS, - /*viewMask*/ 0, - /*inputAttachmentCount*/ 0, - /*pInputAttachments*/ nullptr, - /*colorAttachmentCount*/ 1, - /*pColorAttachments*/ &color_reference, - /*pResolveAttachments*/ nullptr, - /*pDepthStencilAttachment*/ nullptr, - /*preserveAttachmentCount*/ 0, - /*pPreserveAttachments*/ nullptr, - }; - - const VkRenderPassCreateInfo2KHR pass_info = { - /*sType*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, - /*pNext*/ nullptr, - /*flags*/ 0, - /*attachmentCount*/ 1, - /*pAttachments*/ &attachment, - /*subpassCount*/ 1, - /*pSubpasses*/ &subpass, - /*dependencyCount*/ 0, - /*pDependencies*/ nullptr, - /*correlatedViewMaskCount*/ 0, - /*pCorrelatedViewMasks*/ nullptr, - }; - - err = vkCreateRenderPass2KHR(device, &pass_info, nullptr, &window->render_pass); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - const VkFramebufferCreateInfo fb_info = { - /*sType*/ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - /*renderPass*/ window->render_pass, - /*attachmentCount*/ 1, - /*pAttachments*/ &window->swapchain_image_resources[i].view, - /*width*/ (uint32_t)window->width, - /*height*/ (uint32_t)window->height, - /*layers*/ 1, - }; - - err = vkCreateFramebuffer(device, &fb_info, nullptr, &window->swapchain_image_resources[i].framebuffer); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - } - } - - /******** SEPARATE PRESENT QUEUE ************/ - - if (separate_present_queue) { - const VkCommandPoolCreateInfo present_cmd_pool_info = { - /*sType*/ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - /*queueFamilyIndex*/ present_queue_family_index, - }; - err = vkCreateCommandPool(device, &present_cmd_pool_info, nullptr, &window->present_cmd_pool); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - const VkCommandBufferAllocateInfo present_cmd_info = { - /*sType*/ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - /*pNext*/ nullptr, - /*commandPool*/ window->present_cmd_pool, - /*level*/ VK_COMMAND_BUFFER_LEVEL_PRIMARY, - /*commandBufferCount*/ 1, - }; - for (uint32_t i = 0; i < swapchainImageCount; i++) { - err = vkAllocateCommandBuffers(device, &present_cmd_info, - &window->swapchain_image_resources[i].graphics_to_present_cmd); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - const VkCommandBufferBeginInfo cmd_buf_info = { - /*sType*/ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - /*pNext*/ nullptr, - /*flags*/ VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, - /*pInheritanceInfo*/ nullptr, - }; - err = vkBeginCommandBuffer(window->swapchain_image_resources[i].graphics_to_present_cmd, &cmd_buf_info); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - - VkImageMemoryBarrier image_ownership_barrier = { - /*sType*/ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - /*pNext*/ nullptr, - /*srcAccessMask*/ 0, - /*dstAccessMask*/ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - /*oldLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - /*newLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - /*srcQueueFamilyIndex*/ graphics_queue_family_index, - /*dstQueueFamilyIndex*/ present_queue_family_index, - /*image*/ window->swapchain_image_resources[i].image, - /*subresourceRange*/ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } - }; - - vkCmdPipelineBarrier(window->swapchain_image_resources[i].graphics_to_present_cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_ownership_barrier); - err = vkEndCommandBuffer(window->swapchain_image_resources[i].graphics_to_present_cmd); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - } - } - - // Reset current buffer. - window->current_buffer = 0; - - VkSemaphoreCreateInfo semaphoreCreateInfo = { - /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - }; - - for (uint32_t i = 0; i < FRAME_LAG; i++) { - VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]); - ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); - } - - return OK; -} - -Error VulkanContext::initialize() { -#ifdef USE_VOLK - if (volkInitialize() != VK_SUCCESS) { - return FAILED; - } -#endif - - Error err = _create_instance(); - if (err != OK) { - return err; - } - - // Headless? Complete setup now. - if (!_get_platform_surface_extension()) { - err = _create_physical_device(VK_NULL_HANDLE); - if (err != OK) { - return err; - } - - err = _initialize_queues(VK_NULL_HANDLE); - if (err != OK) { - return err; - } - } - - return OK; -} - -void VulkanContext::set_setup_buffer(RDD::CommandBufferID p_command_buffer) { - command_buffer_queue[0] = (VkCommandBuffer)p_command_buffer.id; -} - -void VulkanContext::append_command_buffer(RDD::CommandBufferID p_command_buffer) { - if (command_buffer_queue.size() <= command_buffer_count) { - command_buffer_queue.resize(command_buffer_count + 1); - } - - command_buffer_queue[command_buffer_count] = (VkCommandBuffer)p_command_buffer.id; - command_buffer_count++; -} - -void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending, bool p_sync) { - // Ensure everything else pending is executed. - if (p_sync) { - vkDeviceWaitIdle(device); - } - - // Flush the pending setup buffer. - - bool setup_flushable = p_flush_setup && command_buffer_queue[0]; - bool pending_flushable = p_flush_pending && command_buffer_count > 1; - - if (setup_flushable) { - // Use a fence to wait for everything done. - VkSubmitInfo submit_info; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = nullptr; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = command_buffer_queue.ptr(); - submit_info.signalSemaphoreCount = pending_flushable ? 1 : 0; - submit_info.pSignalSemaphores = pending_flushable ? &draw_complete_semaphores[frame_index] : nullptr; - VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - command_buffer_queue[0] = nullptr; - ERR_FAIL_COND(err); - } - - if (pending_flushable) { - // Use a fence to wait for everything to finish. - - VkSubmitInfo submit_info; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - submit_info.pWaitDstStageMask = setup_flushable ? &wait_stage_mask : nullptr; - submit_info.waitSemaphoreCount = setup_flushable ? 1 : 0; - submit_info.pWaitSemaphores = setup_flushable ? &draw_complete_semaphores[frame_index] : nullptr; - submit_info.commandBufferCount = command_buffer_count - 1; - submit_info.pCommandBuffers = command_buffer_queue.ptr() + 1; - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; - VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - command_buffer_count = 1; - ERR_FAIL_COND(err); - } - - if (p_sync) { - vkDeviceWaitIdle(device); - } -} - -Error VulkanContext::prepare_buffers(RDD::CommandBufferID p_command_buffer) { - if (!queues_initialized) { - return OK; - } - - VkResult err; - - // Ensure no more than FRAME_LAG renderings are outstanding. - vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX); - vkResetFences(device, 1, &fences[frame_index]); - - for (KeyValue &E : windows) { - Window *w = &E.value; - - w->semaphore_acquired = false; - - if (w->swapchain == VK_NULL_HANDLE) { - continue; - } - - do { - // Get the index of the next available swapchain image. - err = - fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, - w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); - - if (err == VK_ERROR_OUT_OF_DATE_KHR) { - // Swapchain is out of date (e.g. the window was resized) and - // must be recreated. - print_verbose("Vulkan: Early out of date swapchain, recreating."); - // resize_notify(); - _update_swap_chain(w); - } else if (err == VK_SUBOPTIMAL_KHR) { - // Swapchain is not as optimal as it could be, but the platform's - // presentation engine will still present the image correctly. - print_verbose("Vulkan: Early suboptimal swapchain, recreating."); - Error swap_chain_err = _update_swap_chain(w); - if (swap_chain_err == ERR_SKIP) { - break; - } - } else if (err != VK_SUCCESS) { - ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err))); - } else { - w->semaphore_acquired = true; - } - } while (err != VK_SUCCESS); - } - - buffers_prepared = true; - - return OK; -} - -void VulkanContext::postpare_buffers(RDD::CommandBufferID p_command_buffer) { -} - -Error VulkanContext::swap_buffers() { - if (!queues_initialized) { - return OK; - } - - //print_line("swap_buffers"); - VkResult err; - -#if 0 - if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { - // Look at what happened to previous presents, and make appropriate - // adjustments in timing. - DemoUpdateTargetIPD(demo); - - // Note: a real application would position its geometry to that it's in - // the correct location for when the next image is presented. It might - // also wait, so that there's less latency between any input and when - // the next image is rendered/presented. This demo program is so - // simple that it doesn't do either of those. - } -#endif - // Wait for the image acquired semaphore to be signaled to ensure - // that the image won't be rendered to until the presentation - // engine has fully released ownership to the application, and it is - // okay to render to the image. - - const VkCommandBuffer *commands_ptr = nullptr; - uint32_t commands_to_submit = 0; - - if (command_buffer_queue[0] == nullptr) { - // No setup command, but commands to submit, submit from the first and skip command. - if (command_buffer_count > 1) { - commands_ptr = command_buffer_queue.ptr() + 1; - commands_to_submit = command_buffer_count - 1; - } - } else { - commands_ptr = command_buffer_queue.ptr(); - commands_to_submit = command_buffer_count; - } - - VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); - VkPipelineStageFlags *pipe_stage_flags = (VkPipelineStageFlags *)alloca(windows.size() * sizeof(VkPipelineStageFlags)); - uint32_t semaphores_to_acquire_count = 0; - - for (KeyValue &E : windows) { - Window *w = &E.value; - - if (w->semaphore_acquired) { - semaphores_to_acquire[semaphores_to_acquire_count] = w->image_acquired_semaphores[frame_index]; - pipe_stage_flags[semaphores_to_acquire_count] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - semaphores_to_acquire_count++; - } - } - - VkSubmitInfo submit_info; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - submit_info.waitSemaphoreCount = semaphores_to_acquire_count; - submit_info.pWaitSemaphores = semaphores_to_acquire; - submit_info.pWaitDstStageMask = pipe_stage_flags; - submit_info.commandBufferCount = commands_to_submit; - submit_info.pCommandBuffers = commands_ptr; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &draw_complete_semaphores[frame_index]; - err = vkQueueSubmit(graphics_queue, 1, &submit_info, fences[frame_index]); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Vulkan: Cannot submit graphics queue. Error code: " + String(string_VkResult(err))); - - command_buffer_queue[0] = nullptr; - command_buffer_count = 1; - - if (separate_present_queue) { - // If we are using separate queues, change image ownership to the - // present queue before presenting, waiting for the draw complete - // semaphore and signaling the ownership released semaphore when finished. - VkFence nullFence = VK_NULL_HANDLE; - pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &draw_complete_semaphores[frame_index]; - submit_info.commandBufferCount = 0; - - VkCommandBuffer *cmdbufptr = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer *) * windows.size()); - submit_info.pCommandBuffers = cmdbufptr; - - for (KeyValue &E : windows) { - Window *w = &E.value; - - if (w->swapchain == VK_NULL_HANDLE) { - continue; - } - cmdbufptr[submit_info.commandBufferCount] = w->swapchain_image_resources[w->current_buffer].graphics_to_present_cmd; - submit_info.commandBufferCount++; - } - - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &image_ownership_semaphores[frame_index]; - err = vkQueueSubmit(present_queue, 1, &submit_info, nullFence); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Vulkan: Cannot submit present queue. Error code: " + String(string_VkResult(err))); - } - - // If we are using separate queues, we have to wait for image ownership, - // otherwise wait for draw complete. - VkPresentInfoKHR present = { - /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - /*pNext*/ nullptr, - /*waitSemaphoreCount*/ 1, - /*pWaitSemaphores*/ (separate_present_queue) ? &image_ownership_semaphores[frame_index] : &draw_complete_semaphores[frame_index], - /*swapchainCount*/ 0, - /*pSwapchain*/ nullptr, - /*pImageIndices*/ nullptr, - /*pResults*/ nullptr, - }; - - VkSwapchainKHR *pSwapchains = (VkSwapchainKHR *)alloca(sizeof(VkSwapchainKHR *) * windows.size()); - uint32_t *pImageIndices = (uint32_t *)alloca(sizeof(uint32_t *) * windows.size()); - - present.pSwapchains = pSwapchains; - present.pImageIndices = pImageIndices; - - for (KeyValue &E : windows) { - Window *w = &E.value; - - if (w->swapchain == VK_NULL_HANDLE) { - continue; - } - pSwapchains[present.swapchainCount] = w->swapchain; - pImageIndices[present.swapchainCount] = w->current_buffer; - present.swapchainCount++; - } - -#if 0 - if (is_device_extension_enabled(VK_KHR_incremental_present_enabled)) { - // If using VK_KHR_incremental_present, we provide a hint of the region - // that contains changed content relative to the previously-presented - // image. The implementation can use this hint in order to save - // work/power (by only copying the region in the hint). The - // implementation is free to ignore the hint though, and so we must - // ensure that the entire image has the correctly-drawn content. - uint32_t eighthOfWidth = width / 8; - uint32_t eighthOfHeight = height / 8; - VkRectLayerKHR rect = { - /*offset.x*/ eighthOfWidth, - /*offset.y*/ eighthOfHeight, - /*extent.width*/ eighthOfWidth * 6, - /*extent.height*/ eighthOfHeight * 6, - /*layer*/ 0, - }; - VkPresentRegionKHR region = { - /*rectangleCount*/ 1, - /*pRectangles*/ &rect, - }; - VkPresentRegionsKHR regions = { - /*sType*/ VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR, - /*pNext*/ present.pNext, - /*swapchainCount*/ present.swapchainCount, - /*pRegions*/ ®ion, - }; - present.pNext = ®ions; - } -#endif - -#if 0 - if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { - VkPresentTimeGOOGLE ptime; - if (prev_desired_present_time == 0) { - // This must be the first present for this swapchain. - // - // We don't know where we are relative to the presentation engine's - // display's refresh cycle. We also don't know how long rendering - // takes. Let's make a grossly-simplified assumption that the - // desiredPresentTime should be half way between now and - // now+target_IPD. We will adjust over time. - uint64_t curtime = getTimeInNanoseconds(); - if (curtime == 0) { - // Since we didn't find out the current time, don't give a - // desiredPresentTime. - ptime.desiredPresentTime = 0; - } else { - ptime.desiredPresentTime = curtime + (target_IPD >> 1); - } - } else { - ptime.desiredPresentTime = (prev_desired_present_time + target_IPD); - } - ptime.presentID = next_present_id++; - prev_desired_present_time = ptime.desiredPresentTime; - - VkPresentTimesInfoGOOGLE present_time = { - /*sType*/ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE, - /*pNext*/ present.pNext, - /*swapchainCount*/ present.swapchainCount, - /*pTimes*/ &ptime, - }; - if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { - present.pNext = &present_time; - } - } -#endif - // print_line("current buffer: " + itos(current_buffer)); - err = fpQueuePresentKHR(present_queue, &present); - - frame_index += 1; - frame_index %= FRAME_LAG; - - if (err == VK_ERROR_OUT_OF_DATE_KHR) { - // Swapchain is out of date (e.g. the window was resized) and - // must be recreated. - print_verbose("Vulkan queue submit: Swapchain is out of date, recreating."); - resize_notify(); - } else if (err == VK_SUBOPTIMAL_KHR) { - // Swapchain is not as optimal as it could be, but the platform's - // presentation engine will still present the image correctly. - print_verbose("Vulkan queue submit: Swapchain is suboptimal."); - } else { - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Error code: " + String(string_VkResult(err))); - } - - buffers_prepared = false; - return OK; -} - -void VulkanContext::resize_notify() { -} - -RenderingDevice::Capabilities VulkanContext::get_device_capabilities() const { - RenderingDevice::Capabilities c; - c.device_family = RenderingDevice::DEVICE_VULKAN; - c.version_major = VK_API_VERSION_MAJOR(device_api_version); - c.version_minor = VK_API_VERSION_MINOR(device_api_version); - return c; -} - -VkDevice VulkanContext::get_device() { - return device; -} - -VkPhysicalDevice VulkanContext::get_physical_device() { - return gpu; -} - -int VulkanContext::get_swapchain_image_count() const { - return swapchainImageCount; -} - -VkQueue VulkanContext::get_graphics_queue() const { - return graphics_queue; -} - -uint32_t VulkanContext::get_graphics_queue_family_index() const { - return graphics_queue_family_index; -} - -VkFormat VulkanContext::get_screen_format() const { - return format; -} - -const VkPhysicalDeviceLimits &VulkanContext::get_device_limits() const { - return gpu_props.limits; -} - -RID VulkanContext::local_device_create() { - LocalDevice ld; - - Error err = _create_device(ld.device); - ERR_FAIL_COND_V(err, RID()); - - { // Create graphics queue. - - vkGetDeviceQueue(ld.device, graphics_queue_family_index, 0, &ld.queue); - } - - ld.driver = memnew(RenderingDeviceDriverVulkan(this, ld.device)); - - return local_device_owner.make_rid(ld); -} - -void VulkanContext::local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - ERR_FAIL_COND(ld->waiting); - - VkSubmitInfo submit_info; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = nullptr; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; - submit_info.commandBufferCount = p_count; - submit_info.pCommandBuffers = (const VkCommandBuffer *)p_buffers; - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; - - VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE); - if (err == VK_ERROR_OUT_OF_HOST_MEMORY) { - print_line("Vulkan: Out of host memory!"); - } - if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) { - print_line("Vulkan: Out of device memory!"); - } - if (err == VK_ERROR_DEVICE_LOST) { - print_line("Vulkan: Device lost!"); - } - ERR_FAIL_COND(err); - - ld->waiting = true; -} - -void VulkanContext::local_device_sync(RID p_local_device) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - ERR_FAIL_COND(!ld->waiting); - - vkDeviceWaitIdle(ld->device); - ld->waiting = false; -} - -void VulkanContext::local_device_free(RID p_local_device) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - memdelete(ld->driver); - vkDestroyDevice(ld->device, nullptr); - local_device_owner.free(p_local_device); -} - -void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - CharString obj_data = p_object_name.utf8(); - VkDebugUtilsObjectNameInfoEXT name_info; - name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; - name_info.pNext = nullptr; - name_info.objectType = p_object_type; - name_info.objectHandle = p_object_handle; - name_info.pObjectName = obj_data.get_data(); - SetDebugUtilsObjectNameEXT(device, &name_info); -} - -String VulkanContext::get_device_vendor_name() const { - return device_vendor; -} - -String VulkanContext::get_device_name() const { - return device_name; -} - -RenderingDevice::DeviceType VulkanContext::get_device_type() const { - return RenderingDevice::DeviceType(device_type); -} - -String VulkanContext::get_device_api_version() const { - return vformat("%d.%d.%d", VK_API_VERSION_MAJOR(device_api_version), VK_API_VERSION_MINOR(device_api_version), VK_API_VERSION_PATCH(device_api_version)); -} - -String VulkanContext::get_device_pipeline_cache_uuid() const { - return pipeline_cache_id; -} - -DisplayServer::VSyncMode VulkanContext::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 VulkanContext::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]); -} - -RenderingDeviceDriver *VulkanContext::get_driver(RID p_local_device) { - if (p_local_device.is_valid()) { - LocalDevice *ld = local_device_owner.get_or_null(p_local_device); - ERR_FAIL_NULL_V(ld, nullptr); - return ld->driver; - } else { - return driver; - } -} - -bool VulkanContext::is_debug_utils_enabled() const { - return is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); -} - -VulkanContext::VulkanContext() { - command_buffer_queue.resize(1); // First one is always the setup command. - command_buffer_queue[0] = nullptr; -} - -VulkanContext::~VulkanContext() { - if (driver) { - memdelete(driver); - } - if (queue_props) { - free(queue_props); - } - if (device_initialized) { - for (uint32_t i = 0; i < FRAME_LAG; i++) { - vkDestroyFence(device, fences[i], nullptr); - vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr); - if (separate_present_queue) { - vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); - } - } - if (inst_initialized && is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr); - } - if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) { - DestroyDebugReportCallbackEXT(inst, dbg_debug_report, nullptr); - } - vkDestroyDevice(device, nullptr); - } - if (inst_initialized) { - vkDestroyInstance(inst, nullptr); - } -} diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h deleted file mode 100644 index cbb6cf326fa..00000000000 --- a/drivers/vulkan/vulkan_context.h +++ /dev/null @@ -1,348 +0,0 @@ -/**************************************************************************/ -/* vulkan_context.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. */ -/**************************************************************************/ - -#ifndef VULKAN_CONTEXT_H -#define VULKAN_CONTEXT_H - -#include "core/error/error_list.h" -#include "core/os/mutex.h" -#include "core/string/ustring.h" -#include "core/templates/hash_map.h" -#include "core/templates/rb_map.h" -#include "core/templates/rid_owner.h" -#include "rendering_device_driver_vulkan.h" -#include "servers/display_server.h" -#include "servers/rendering/renderer_rd/api_context_rd.h" - -#ifdef USE_VOLK -#include -#else -#include -#endif - -#include "vulkan_hooks.h" - -class VulkanContext : public ApiContextRD { -public: - struct SubgroupCapabilities { - uint32_t size; - uint32_t min_size; - uint32_t max_size; - VkShaderStageFlags supportedStages; - VkSubgroupFeatureFlags supportedOperations; - VkBool32 quadOperationsInAllStages; - bool size_control_is_supported; - - uint32_t supported_stages_flags_rd() const; - String supported_stages_desc() const; - uint32_t supported_operations_flags_rd() const; - String supported_operations_desc() const; - }; - - struct VRSCapabilities { - bool pipeline_vrs_supported; // We can specify our fragment rate on a pipeline level. - bool primitive_vrs_supported; // We can specify our fragment rate on each drawcall. - bool attachment_vrs_supported; // We can provide a density map attachment on our framebuffer. - - Size2i min_texel_size; - Size2i max_texel_size; - - Size2i texel_size; // The texel size we'll use - }; - - struct ShaderCapabilities { - bool shader_float16_is_supported; - bool shader_int8_is_supported; - }; - - struct StorageBufferCapabilities { - bool storage_buffer_16_bit_access_is_supported; - bool uniform_and_storage_buffer_16_bit_access_is_supported; - bool storage_push_constant_16_is_supported; - bool storage_input_output_16; - }; - -private: - enum { - MAX_EXTENSIONS = 128, - MAX_LAYERS = 64, - FRAME_LAG = 2 - }; - - static VulkanHooks *vulkan_hooks; - VkInstance inst = VK_NULL_HANDLE; - VkPhysicalDevice gpu = VK_NULL_HANDLE; - VkPhysicalDeviceProperties gpu_props; - uint32_t queue_family_count = 0; - VkQueueFamilyProperties *queue_props = nullptr; - VkDevice device = VK_NULL_HANDLE; - bool device_initialized = false; - bool inst_initialized = false; - - uint32_t instance_api_version = VK_API_VERSION_1_0; - SubgroupCapabilities subgroup_capabilities; - RDD::MultiviewCapabilities multiview_capabilities; - VRSCapabilities vrs_capabilities; - ShaderCapabilities shader_capabilities; - StorageBufferCapabilities storage_buffer_capabilities; - bool pipeline_cache_control_support = false; - - String device_vendor; - String device_name; - VkPhysicalDeviceType device_type; - String pipeline_cache_id; - uint32_t device_api_version = 0; - - bool buffers_prepared = false; - - // Present queue. - bool queues_initialized = false; - uint32_t graphics_queue_family_index = UINT32_MAX; - uint32_t present_queue_family_index = UINT32_MAX; - bool separate_present_queue = false; - VkQueue graphics_queue = VK_NULL_HANDLE; - VkQueue present_queue = VK_NULL_HANDLE; - VkColorSpaceKHR color_space; - VkFormat format; - VkSemaphore draw_complete_semaphores[FRAME_LAG]; - VkSemaphore image_ownership_semaphores[FRAME_LAG]; - int frame_index = 0; - VkFence fences[FRAME_LAG]; - VkPhysicalDeviceMemoryProperties memory_properties; - VkPhysicalDeviceFeatures physical_device_features; - - typedef struct { - VkImage image; - VkCommandBuffer graphics_to_present_cmd; - VkImageView view; - VkFramebuffer framebuffer; - } SwapchainImageResources; - - struct Window { - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkSwapchainKHR swapchain = VK_NULL_HANDLE; - SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE; - VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; - VkSemaphore image_acquired_semaphores[FRAME_LAG]; - bool semaphore_acquired = false; - uint32_t current_buffer = 0; - int width = 0; - int height = 0; - DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; - VkCommandPool present_cmd_pool = VK_NULL_HANDLE; // For separate present queue. - VkRenderPass render_pass = VK_NULL_HANDLE; - }; - - struct LocalDevice { - bool waiting = false; - VkDevice device = VK_NULL_HANDLE; - VkQueue queue = VK_NULL_HANDLE; - RenderingDeviceDriverVulkan *driver = nullptr; - }; - - RID_Owner local_device_owner; - - RenderingDeviceDriverVulkan *driver = nullptr; - - HashMap windows; - uint32_t swapchainImageCount = 0; - - // Commands. - - bool prepared = false; - - LocalVector command_buffer_queue; - uint32_t command_buffer_count = 1; - - // Extensions. - static bool instance_extensions_initialized; - static HashMap requested_instance_extensions; - HashSet enabled_instance_extension_names; - - static bool device_extensions_initialized; - static HashMap requested_device_extensions; - HashSet enabled_device_extension_names; - bool VK_KHR_incremental_present_enabled = true; - bool VK_GOOGLE_display_timing_enabled = true; - - PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT = nullptr; - PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT = nullptr; - PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT = nullptr; - PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT = nullptr; - PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT = nullptr; - PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT = nullptr; - PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT = nullptr; - PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; - PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; - PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; - PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR = nullptr; - PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR = nullptr; - PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR = nullptr; - PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR = nullptr; - PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR = nullptr; - PFN_vkQueuePresentKHR fpQueuePresentKHR = nullptr; - PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE = nullptr; - PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE = nullptr; - PFN_vkCreateRenderPass2KHR fpCreateRenderPass2KHR = nullptr; - - VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; - VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; - - Error _obtain_vulkan_version(); - Error _initialize_instance_extensions(); - Error _initialize_device_extensions(); - Error _check_capabilities(); - - VkBool32 _check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers); - static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, - void *pUserData); - - static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char *pLayerPrefix, - const char *pMessage, - void *pUserData); - - Error _create_instance(); - - Error _create_physical_device(VkSurfaceKHR p_surface); - - Error _initialize_queues(VkSurfaceKHR p_surface); - - Error _create_device(VkDevice &r_vk_device); - - Error _clean_up_swap_chain(Window *window); - - Error _update_swap_chain(Window *window); - - Error _create_swap_chain(); - Error _create_semaphores(); - - Vector _convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs); - -protected: - virtual const char *_get_platform_surface_extension() const { return nullptr; } - - virtual Error _window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height); - - virtual bool _use_validation_layers(); - - Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); - - virtual VkExtent2D _compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const; - -public: - // Extension calls. - bool supports_renderpass2() const { return is_device_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); } - VkResult vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass); - - virtual const char *get_api_name() const override final { return "Vulkan"; }; - virtual RenderingDevice::Capabilities get_device_capabilities() const override final; - const SubgroupCapabilities &get_subgroup_capabilities() const { return subgroup_capabilities; }; - virtual const RDD::MultiviewCapabilities &get_multiview_capabilities() const override final { return multiview_capabilities; }; - const VRSCapabilities &get_vrs_capabilities() const { return vrs_capabilities; }; - const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; }; - const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; - const VkPhysicalDeviceFeatures &get_physical_device_features() const { return physical_device_features; }; - bool get_pipeline_cache_control_support() const { return pipeline_cache_control_support; }; - - VkDevice get_device(); - VkPhysicalDevice get_physical_device(); - VkInstance get_instance() { return inst; } - virtual int get_swapchain_image_count() const override final; - VkQueue get_graphics_queue() const; - uint32_t get_graphics_queue_family_index() const; - - static void set_vulkan_hooks(VulkanHooks *p_vulkan_hooks) { vulkan_hooks = p_vulkan_hooks; }; - - static void register_requested_instance_extension(const CharString &extension_name, bool p_required); - bool is_instance_extension_enabled(const CharString &extension_name) const { - return enabled_instance_extension_names.has(extension_name); - } - - static void register_requested_device_extension(const CharString &extension_name, bool p_required); - bool is_device_extension_enabled(const CharString &extension_name) const { - return enabled_device_extension_names.has(extension_name); - } - - virtual void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) override final; - virtual int window_get_width(DisplayServer::WindowID p_window = 0) override final; - virtual int window_get_height(DisplayServer::WindowID p_window = 0) override final; - virtual bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0) override final; - virtual void window_destroy(DisplayServer::WindowID p_window_id) override final; - virtual RDD::RenderPassID window_get_render_pass(DisplayServer::WindowID p_window = 0) override final; - virtual RDD::FramebufferID window_get_framebuffer(DisplayServer::WindowID p_window = 0) override final; - - virtual RID local_device_create() override final; - virtual void local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) override final; - virtual void local_device_sync(RID p_local_device) override final; - virtual void local_device_free(RID p_local_device) override final; - - VkFormat get_screen_format() const; - const VkPhysicalDeviceLimits &get_device_limits() const; - - virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) override final; - virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) override final; - void resize_notify(); - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) override final; - virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) override final; - virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) override final; - virtual Error swap_buffers() override final; - virtual Error initialize() override final; - - void set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name); - - virtual String get_device_vendor_name() const override final; - virtual String get_device_name() const override final; - virtual RDD::DeviceType get_device_type() const override final; - virtual String get_device_api_version() const override final; - virtual String get_device_pipeline_cache_uuid() const override final; - - virtual void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) override final; - virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const override final; - - virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) override final; - virtual bool is_debug_utils_enabled() const override final; - - VulkanContext(); - virtual ~VulkanContext(); -}; - -#endif // VULKAN_CONTEXT_H diff --git a/servers/rendering/renderer_rd/api_context_rd.cpp b/drivers/vulkan/vulkan_hooks.cpp similarity index 87% rename from servers/rendering/renderer_rd/api_context_rd.cpp rename to drivers/vulkan/vulkan_hooks.cpp index b5b3cdd88c4..416efcae807 100644 --- a/servers/rendering/renderer_rd/api_context_rd.cpp +++ b/drivers/vulkan/vulkan_hooks.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* api_context_rd.cpp */ +/* vulkan_hooks.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,6 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "api_context_rd.h" +#include "vulkan_hooks.h" -ApiContextRD::~ApiContextRD() {} +VulkanHooks *VulkanHooks::singleton = nullptr; + +VulkanHooks::VulkanHooks() { + if (singleton == nullptr) { + singleton = this; + } +} + +VulkanHooks::~VulkanHooks() { + if (singleton == this) { + singleton = nullptr; + } +} diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h index eaa52658e4c..bb30b29cec9 100644 --- a/drivers/vulkan/vulkan_hooks.h +++ b/drivers/vulkan/vulkan_hooks.h @@ -38,11 +38,17 @@ #endif class VulkanHooks { +private: + static VulkanHooks *singleton; + public: - virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) { return false; }; - virtual bool get_physical_device(VkPhysicalDevice *r_device) { return false; }; - virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) { return false; }; - virtual ~VulkanHooks(){}; + VulkanHooks(); + virtual ~VulkanHooks(); + virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) = 0; + virtual bool get_physical_device(VkPhysicalDevice *r_device) = 0; + virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) = 0; + virtual void set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) = 0; + static VulkanHooks *get_singleton() { return singleton; } }; #endif // VULKAN_HOOKS_H diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 88622364d8e..a9390487ef6 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -113,3 +113,10 @@ Validate extension JSON: API was removed: classes/TileMap/methods/set_tileset Validate extension JSON: API was removed: classes/TileMap/properties/tile_set Moved to the parent TileMapLayerGroup class. No change should be necessary. + + +GH-87340 +-------- +Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RenderingDevice/methods/screen_get_framebuffer_format': arguments + +screen_get_framebuffer_format can now specify the screen it should get the format from. The argument defaults to the main window to emulate the behavior of the old function. diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index d7e5ddd32cd..09ad1d67773 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -39,7 +39,7 @@ #include static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device) { - const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); + const RDD::Capabilities &capabilities = p_render_device->get_device_capabilities(); Vector ret; ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret); @@ -57,17 +57,17 @@ static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2; glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; - if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { - if (capabilities->version_major == 1 && capabilities->version_minor == 0) { + if (capabilities.device_family == RDD::DEVICE_VULKAN) { + if (capabilities.version_major == 1 && capabilities.version_minor == 0) { ClientVersion = glslang::EShTargetVulkan_1_0; TargetVersion = glslang::EShTargetSpv_1_0; - } else if (capabilities->version_major == 1 && capabilities->version_minor == 1) { + } else if (capabilities.version_major == 1 && capabilities.version_minor == 1) { ClientVersion = glslang::EShTargetVulkan_1_1; TargetVersion = glslang::EShTargetSpv_1_3; } else { // use defaults } - } else if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_DIRECTX) { + } else if (capabilities.device_family == RDD::DEVICE_DIRECTX) { // NIR-DXIL is Vulkan 1.1-conformant. ClientVersion = glslang::EShTargetVulkan_1_1; // The SPIR-V part of Mesa supports 1.6, but: @@ -186,9 +186,9 @@ static Vector _compile_shader_glsl(RenderingDevice::ShaderStage p_stage } static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) { - const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); + const RenderingDeviceDriver::Capabilities &capabilities = p_render_device->get_device_capabilities(); String version; - version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled()); + version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities.version_major) + ", minor=" + itos(capabilities.version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled()); return version; } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 9ee281ad996..5d22cb1301c 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -41,6 +41,10 @@ #include "editor/editor_settings.h" #include "servers/rendering/rendering_device_binds.h" +#if defined(VULKAN_ENABLED) +#include "drivers/vulkan/rendering_context_driver_vulkan.h" +#endif + //uncomment this if you want to see textures from all the process saved //#define DEBUG_TEXTURES @@ -1017,7 +1021,35 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } #endif - RenderingDevice *rd = RenderingDevice::get_singleton()->create_local_device(); + // Attempt to create a local device by requesting it from rendering server first. + // If that fails because the current renderer is not implemented on top of RD, we fall back to creating + // a local rendering device manually depending on the current platform. + Error err; + RenderingContextDriver *rcd = nullptr; + RenderingDevice *rd = RenderingServer::get_singleton()->create_local_rendering_device(); + if (rd == nullptr) { +#if defined(RD_ENABLED) +#if defined(VULKAN_ENABLED) + rcd = memnew(RenderingContextDriverVulkan); + rd = memnew(RenderingDevice); +#endif +#endif + if (rcd != nullptr && rd != nullptr) { + err = rcd->initialize(); + if (err == OK) { + err = rd->initialize(rcd); + } + + if (err != OK) { + memdelete(rd); + memdelete(rcd); + rd = nullptr; + rcd = nullptr; + } + } + } + + ERR_FAIL_NULL_V(rd, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); RID albedo_array_tex; RID emission_array_tex; @@ -1187,7 +1219,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d //shaders Ref raster_shader; raster_shader.instantiate(); - Error err = raster_shader->parse_versions_from_text(lm_raster_shader_glsl); + err = raster_shader->parse_versions_from_text(lm_raster_shader_glsl); if (err != OK) { raster_shader->print_errors("raster_shader"); @@ -1195,6 +1227,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d FREE_BUFFERS memdelete(rd); + + if (rcd != nullptr) { + memdelete(rcd); + } } ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); @@ -1367,6 +1403,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d FREE_BUFFERS FREE_RASTER_RESOURCES memdelete(rd); + + if (rcd != nullptr) { + memdelete(rcd); + } + compute_shader->print_errors("compute_shader"); } ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); @@ -1789,6 +1830,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d FREE_RASTER_RESOURCES FREE_COMPUTE_RESOURCES memdelete(rd); + + if (rcd != nullptr) { + memdelete(rcd); + } + blendseams_shader->print_errors("blendseams_shader"); } ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); @@ -1964,6 +2010,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d memdelete(rd); + if (rcd != nullptr) { + memdelete(rcd); + } + return BAKE_OK; } diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp index a2f25779591..f5e7fc192c2 100644 --- a/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp +++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.cpp @@ -38,14 +38,6 @@ #include "servers/rendering/rendering_server_globals.h" #include "servers/rendering_server.h" -OpenXRVulkanExtension::OpenXRVulkanExtension() { - VulkanContext::set_vulkan_hooks(this); -} - -OpenXRVulkanExtension::~OpenXRVulkanExtension() { - VulkanContext::set_vulkan_hooks(nullptr); -} - HashMap OpenXRVulkanExtension::get_requested_extensions() { HashMap request_extensions; @@ -178,10 +170,6 @@ bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) { bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) { ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false); - // the first entry in our queue list should be the one we need to remember... - vulkan_queue_family_index = p_device_create_info->pQueueCreateInfos[0].queueFamilyIndex; - vulkan_queue_index = 0; // ?? - XrVulkanDeviceCreateInfoKHR create_info = { XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, // type nullptr, // next @@ -209,9 +197,17 @@ bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_dev return true; } +void OpenXRVulkanExtension::set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) { + vulkan_queue_family_index = p_queue_family_index; + vulkan_queue_index = p_queue_index; +} + XrGraphicsBindingVulkanKHR OpenXRVulkanExtension::graphics_binding_vulkan; void *OpenXRVulkanExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) { + DEV_ASSERT(vulkan_queue_family_index < UINT32_MAX && "Direct queue family index was not specified yet."); + DEV_ASSERT(vulkan_queue_index < UINT32_MAX && "Direct queue index was not specified yet."); + graphics_binding_vulkan.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR; graphics_binding_vulkan.next = p_next_pointer; graphics_binding_vulkan.instance = vulkan_instance; diff --git a/modules/openxr/extensions/platform/openxr_vulkan_extension.h b/modules/openxr/extensions/platform/openxr_vulkan_extension.h index 2d0973bb6bf..a3f86a9968b 100644 --- a/modules/openxr/extensions/platform/openxr_vulkan_extension.h +++ b/modules/openxr/extensions/platform/openxr_vulkan_extension.h @@ -36,23 +36,25 @@ #include "../openxr_extension_wrapper.h" #include "core/templates/vector.h" +#include "drivers/vulkan/vulkan_hooks.h" // Always include this as late as possible. #include "../../openxr_platform_inc.h" class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks { public: - OpenXRVulkanExtension(); - virtual ~OpenXRVulkanExtension() override; + OpenXRVulkanExtension() = default; + virtual ~OpenXRVulkanExtension() override = default; virtual HashMap 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 bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) override; - virtual bool get_physical_device(VkPhysicalDevice *r_device) override; - virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override; + virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) override final; + virtual bool get_physical_device(VkPhysicalDevice *r_device) override final; + virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override final; + virtual void set_direct_queue_family_and_index(uint32_t p_queue_family_index, uint32_t p_queue_index) override final; virtual void get_usable_swapchain_formats(Vector &p_usable_swap_chains) override; virtual void get_usable_depth_formats(Vector &p_usable_swap_chains) override; @@ -76,8 +78,8 @@ private: VkInstance vulkan_instance = nullptr; VkPhysicalDevice vulkan_physical_device = nullptr; VkDevice vulkan_device = nullptr; - uint32_t vulkan_queue_family_index = 0; - uint32_t vulkan_queue_index = 0; + uint32_t vulkan_queue_family_index = UINT32_MAX; + uint32_t vulkan_queue_index = UINT32_MAX; EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsRequirements2KHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsVulkanKHR *), p_graphics_requirements) EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanInstanceKHR, (XrInstance), p_instance, (const XrVulkanInstanceCreateInfoKHR *), p_create_info, (VkInstance *), r_vulkan_instance, (VkResult *), r_vulkan_result) diff --git a/modules/openxr/openxr_platform_inc.h b/modules/openxr/openxr_platform_inc.h index 6288d1e380d..957a87cbb21 100644 --- a/modules/openxr/openxr_platform_inc.h +++ b/modules/openxr/openxr_platform_inc.h @@ -36,7 +36,7 @@ #ifdef VULKAN_ENABLED #define XR_USE_GRAPHICS_API_VULKAN -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #endif // VULKAN_ENABLED #if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED) diff --git a/platform/android/SCsub b/platform/android/SCsub index 97262cf148b..31bc7c25b04 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -23,7 +23,7 @@ android_files = [ "android_keys_utils.cpp", "display_server_android.cpp", "plugin/godot_plugin_jni.cpp", - "vulkan_context_android.cpp", + "rendering_context_driver_vulkan_android.cpp", ] env_android = env.Clone() diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 4a9915379ae..b06164246eb 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -42,7 +42,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#include "vulkan_context_android.h" +#include "rendering_context_driver_vulkan_android.h" #endif #endif @@ -518,15 +518,17 @@ void DisplayServerAndroid::register_android_driver() { void DisplayServerAndroid::reset_window() { #if defined(RD_ENABLED) - if (context_rd) { - VSyncMode last_vsync_mode = context_rd->get_vsync_mode(MAIN_WINDOW_ID); - context_rd->window_destroy(MAIN_WINDOW_ID); + if (rendering_context) { + if (rendering_device) { + rendering_device->screen_free(MAIN_WINDOW_ID); + } - Size2i display_size = OS_Android::get_singleton()->get_display_size(); + VSyncMode last_vsync_mode = rendering_context->window_get_vsync_mode(MAIN_WINDOW_ID); + rendering_context->window_destroy(MAIN_WINDOW_ID); union { #ifdef VULKAN_ENABLED - VulkanContextAndroid::WindowPlatformData vulkan; + RenderingContextDriverVulkanAndroid::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -537,12 +539,20 @@ void DisplayServerAndroid::reset_window() { } #endif - if (context_rd->window_create(MAIN_WINDOW_ID, last_vsync_mode, display_size.width, display_size.height, &wpd) != OK) { - ERR_PRINT(vformat("Failed to reset %s window.", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) { + ERR_PRINT(vformat("Failed to reset %s window.", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; return; } + + Size2i display_size = OS_Android::get_singleton()->get_display_size(); + rendering_context->window_set_size(MAIN_WINDOW_ID, display_size.width, display_size.height); + rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, last_vsync_mode); + + if (rendering_device) { + rendering_device->screen_create(MAIN_WINDOW_ID); + } } #endif } @@ -565,28 +575,26 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis #endif #if defined(RD_ENABLED) - context_rd = nullptr; + rendering_context = nullptr; rendering_device = nullptr; #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextAndroid); + rendering_context = memnew(RenderingContextDriverVulkanAndroid); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Failed to initialize %s context", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; return; } - Size2i display_size = OS_Android::get_singleton()->get_display_size(); - union { #ifdef VULKAN_ENABLED - VulkanContextAndroid::WindowPlatformData vulkan; + RenderingContextDriverVulkanAndroid::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -597,15 +605,20 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis } #endif - if (context_rd->window_create(MAIN_WINDOW_ID, p_vsync_mode, display_size.width, display_size.height, &wpd) != OK) { - ERR_PRINT(vformat("Failed to create %s window.", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) { + ERR_PRINT(vformat("Failed to create %s window.", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; return; } + Size2i display_size = OS_Android::get_singleton()->get_display_size(); + rendering_context->window_set_size(MAIN_WINDOW_ID, display_size.width, display_size.height); + rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode); + rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -620,11 +633,10 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis DisplayServerAndroid::~DisplayServerAndroid() { #if defined(RD_ENABLED) if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); } - if (context_rd) { - memdelete(context_rd); + if (rendering_context) { + memdelete(rendering_context); } #endif } @@ -716,16 +728,16 @@ void DisplayServerAndroid::cursor_set_custom_image(const Ref &p_cursor void DisplayServerAndroid::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { #if defined(RD_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif } DisplayServer::VSyncMode DisplayServerAndroid::window_get_vsync_mode(WindowID p_window) const { #if defined(RD_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif return DisplayServer::VSYNC_ENABLED; diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 80af4f00c18..b425b2d9ae5 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -34,7 +34,7 @@ #include "servers/display_server.h" #if defined(RD_ENABLED) -class ApiContextRD; +class RenderingContextDriver; class RenderingDevice; #endif @@ -73,7 +73,7 @@ class DisplayServerAndroid : public DisplayServer { CursorShape cursor_shape = CursorShape::CURSOR_ARROW; #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif diff --git a/platform/android/vulkan_context_android.cpp b/platform/android/rendering_context_driver_vulkan_android.cpp similarity index 69% rename from platform/android/vulkan_context_android.cpp rename to platform/android/rendering_context_driver_vulkan_android.cpp index 4f9140bf3ef..9232126b047 100644 --- a/platform/android/vulkan_context_android.cpp +++ b/platform/android/rendering_context_driver_vulkan_android.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_android.cpp */ +/* rendering_context_driver_vulkan_android.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "vulkan_context_android.h" +#include "rendering_context_driver_vulkan_android.h" #ifdef VULKAN_ENABLED @@ -38,32 +38,32 @@ #include #endif -const char *VulkanContextAndroid::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanAndroid::_get_platform_surface_extension() const { return VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; } -Error VulkanContextAndroid::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAndroid::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkAndroidSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - createInfo.window = wpd->window; + VkAndroidSurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + create_info.window = wpd->window; - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateAndroidSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - if (err != VK_SUCCESS) { - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkCreateAndroidSurfaceKHR failed with error " + itos(err)); - } + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateAndroidSurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); - return _window_create(DisplayServer::MAIN_WINDOW_ID, p_vsync_mode, surface, p_width, p_height); + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } -bool VulkanContextAndroid::_use_validation_layers() { - uint32_t count = 0; - _get_preferred_validation_layers(&count, nullptr); +bool RenderingContextDriverVulkanAndroid::_use_validation_layers() const { + TightLocalVector layer_names; + Error err = _find_validation_layers(layer_names); // On Android, we use validation layers automatically if they were explicitly linked with the app. - return count > 0; + return (err == OK) && !layer_names.is_empty(); } #endif // VULKAN_ENABLED diff --git a/platform/android/vulkan_context_android.h b/platform/android/rendering_context_driver_vulkan_android.h similarity index 80% rename from platform/android/vulkan_context_android.h rename to platform/android/rendering_context_driver_vulkan_android.h index 3cee3feb867..a2a42eef249 100644 --- a/platform/android/vulkan_context_android.h +++ b/platform/android/rendering_context_driver_vulkan_android.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_android.h */ +/* rendering_context_driver_vulkan_android.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,32 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_ANDROID_H -#define VULKAN_CONTEXT_ANDROID_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" struct ANativeWindow; -class VulkanContextAndroid : public VulkanContext { +class RenderingContextDriverVulkanAndroid : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + bool _use_validation_layers() const override final; + public: struct WindowPlatformData { ANativeWindow *window; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextAndroid() = default; - ~VulkanContextAndroid() override = default; - -protected: - bool _use_validation_layers() override; + RenderingContextDriverVulkanAndroid() = default; + ~RenderingContextDriverVulkanAndroid() override = default; }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_ANDROID_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_ANDROID_H diff --git a/platform/ios/SCsub b/platform/ios/SCsub index 30d4216464e..d7c950967c0 100644 --- a/platform/ios/SCsub +++ b/platform/ios/SCsub @@ -9,7 +9,7 @@ ios_lib = [ "app_delegate.mm", "view_controller.mm", "ios.mm", - "vulkan_context_ios.mm", + "rendering_context_driver_vulkan_ios.mm", "display_server_ios.mm", "joypad_ios.mm", "godot_view.mm", diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 3fdcc07f0ba..3efd2498d48 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -39,7 +39,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#import "vulkan_context_ios.h" +#import "rendering_context_driver_vulkan_ios.h" #ifdef USE_VOLK #include @@ -62,7 +62,7 @@ class DisplayServerIOS : public DisplayServer { _THREAD_SAFE_CLASS_ #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 9c671f2ea94..c371f2777cb 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -63,14 +63,14 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode } #if defined(RD_ENABLED) - context_rd = nullptr; + rendering_context = nullptr; rendering_device = nullptr; CALayer *layer = nullptr; union { #ifdef VULKAN_ENABLED - VulkanContextIOS::WindowPlatformData vulkan; + RenderingContextDriverVulkanIOS::WindowPlatformData vulkan; #endif } wpd; @@ -81,29 +81,33 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode ERR_FAIL_MSG("Failed to create iOS Vulkan rendering layer."); } wpd.vulkan.layer_ptr = &layer; - context_rd = memnew(VulkanContextIOS); + rendering_context = memnew(RenderingContextDriverVulkanIOS); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Failed to initialize %s context", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; return; } - Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale(); - if (context_rd->window_create(MAIN_WINDOW_ID, p_vsync_mode, size.width, size.height, &wpd) != OK) { - ERR_PRINT(vformat("Failed to create %s window.", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) { + ERR_PRINT(vformat("Failed to create %s window.", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_UNAVAILABLE; return; } + Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale(); + rendering_context->window_set_size(MAIN_WINDOW_ID, size.width, size.height); + rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode); + rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -132,15 +136,15 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode DisplayServerIOS::~DisplayServerIOS() { #if defined(RD_ENABLED) if (rendering_device) { - rendering_device->finalize(); + rendering_device->screen_free(MAIN_WINDOW_ID); memdelete(rendering_device); rendering_device = nullptr; } - if (context_rd) { - context_rd->window_destroy(MAIN_WINDOW_ID); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + rendering_context->window_destroy(MAIN_WINDOW_ID); + memdelete(rendering_context); + rendering_context = nullptr; } #endif } @@ -714,8 +718,8 @@ void DisplayServerIOS::resize_window(CGSize viewSize) { Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale(); #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(MAIN_WINDOW_ID, size.x, size.y); + if (rendering_context) { + rendering_context->window_set_size(MAIN_WINDOW_ID, size.x, size.y); } #endif @@ -726,8 +730,8 @@ void DisplayServerIOS::resize_window(CGSize viewSize) { void DisplayServerIOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif } @@ -735,8 +739,8 @@ void DisplayServerIOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo DisplayServer::VSyncMode DisplayServerIOS::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif return DisplayServer::VSYNC_ENABLED; diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index 06724d763fa..2d985a6c0bc 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -45,7 +45,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#import "vulkan_context_ios.h" +#import "rendering_context_driver_vulkan_ios.h" #endif #endif diff --git a/platform/ios/vulkan_context_ios.h b/platform/ios/rendering_context_driver_vulkan_ios.h similarity index 82% rename from platform/ios/vulkan_context_ios.h rename to platform/ios/rendering_context_driver_vulkan_ios.h index cdc8b618af6..0778993a05d 100644 --- a/platform/ios/vulkan_context_ios.h +++ b/platform/ios/rendering_context_driver_vulkan_ios.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_ios.h */ +/* rendering_context_driver_vulkan_ios.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,28 +28,31 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_IOS_H -#define VULKAN_CONTEXT_IOS_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_IOS_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_IOS_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #import -class VulkanContextIOS : public VulkanContext { +class RenderingContextDriverVulkanIOS : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { CALayer *const *layer_ptr; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextIOS(); - ~VulkanContextIOS(); + RenderingContextDriverVulkanIOS(); + ~RenderingContextDriverVulkanIOS(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_IOS_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_IOS_H diff --git a/platform/ios/vulkan_context_ios.mm b/platform/ios/rendering_context_driver_vulkan_ios.mm similarity index 69% rename from platform/ios/vulkan_context_ios.mm rename to platform/ios/rendering_context_driver_vulkan_ios.mm index 014e05f2e6c..7e9c3e0e441 100644 --- a/platform/ios/vulkan_context_ios.mm +++ b/platform/ios/rendering_context_driver_vulkan_ios.mm @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_ios.mm */ +/* rendering_context_driver_vulkan_ios.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#import "vulkan_context_ios.h" +#import "rendering_context_driver_vulkan_ios.h" #ifdef VULKAN_ENABLED @@ -38,26 +38,32 @@ #include #endif -const char *VulkanContextIOS::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanIOS::_get_platform_surface_extension() const { return VK_MVK_IOS_SURFACE_EXTENSION_NAME; } -Error VulkanContextIOS::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanIOS::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkIOSSurfaceCreateInfoMVK createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; - createInfo.pView = (__bridge const void *)(*wpd->layer_ptr); + VkIOSSurfaceCreateInfoMVK create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; + create_info.pView = (__bridge const void *)(*wpd->layer_ptr); - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateIOSSurfaceMVK(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateIOSSurfaceMVK(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } -VulkanContextIOS::VulkanContextIOS() {} +RenderingContextDriverVulkanIOS::RenderingContextDriverVulkanIOS() { + // Does nothing. +} -VulkanContextIOS::~VulkanContextIOS() {} +RenderingContextDriverVulkanIOS::~RenderingContextDriverVulkanIOS() { + // Does nothing. +} #endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub index 910ac29b5ea..cab45b76723 100644 --- a/platform/linuxbsd/wayland/SCsub +++ b/platform/linuxbsd/wayland/SCsub @@ -195,7 +195,7 @@ if env["use_sowrap"]: if env["vulkan"]: - source_files.append("vulkan_context_wayland.cpp") + source_files.append("rendering_context_driver_vulkan_wayland.cpp") if env["opengl3"]: source_files.append("egl_manager_wayland.cpp") diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 02b715056a1..b8a10ea6b9e 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -100,8 +100,8 @@ void DisplayServerWayland::_resize_window(const Size2i &p_size) { wd.rect.size = p_size; #ifdef RD_ENABLED - if (wd.visible && context_rd) { - context_rd->window_resize(MAIN_WINDOW_ID, wd.rect.size.width, wd.rect.size.height); + if (wd.visible && rendering_context) { + rendering_context->window_set_size(MAIN_WINDOW_ID, wd.rect.size.width, wd.rect.size.height); } #endif @@ -140,10 +140,10 @@ void DisplayServerWayland::_show_window() { // the only acceptable way of implementing window showing is to move the // graphics context window creation logic here. #ifdef RD_ENABLED - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextWayland::WindowPlatformData vulkan; + RenderingContextDriverVulkanWayland::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -152,14 +152,17 @@ void DisplayServerWayland::_show_window() { wpd.vulkan.display = wayland_thread.get_wl_display(); } #endif - Error err = context_rd->window_create(wd.id, wd.vsync_mode, wd.rect.size.width, wd.rect.size.height, &wpd); - ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", context_rd->get_api_name())); + Error err = rendering_context->window_create(wd.id, &wpd); + ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver)); - emulate_vsync = (context_rd->get_vsync_mode(wd.id) == DisplayServer::VSYNC_ENABLED); + rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height); + rendering_context->window_set_vsync_mode(wd.id, wd.vsync_mode); + + emulate_vsync = (rendering_context->window_get_vsync_mode(wd.id) == DisplayServer::VSYNC_ENABLED); if (emulate_vsync) { print_verbose("VSYNC: manually throttling frames using MAILBOX."); - context_rd->set_vsync_mode(wd.id, DisplayServer::VSYNC_MAILBOX); + rendering_context->window_set_vsync_mode(wd.id, DisplayServer::VSYNC_MAILBOX); } } #endif @@ -885,14 +888,14 @@ void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn MutexLock mutex_lock(wayland_thread.mutex); #ifdef RD_ENABLED - if (context_rd) { - context_rd->set_vsync_mode(p_window_id, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode); - emulate_vsync = (context_rd->get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED); + emulate_vsync = (rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED); if (emulate_vsync) { print_verbose("VSYNC: manually throttling frames using MAILBOX."); - context_rd->set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX); + rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX); } } #endif // VULKAN_ENABLED @@ -917,8 +920,8 @@ DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServ } #ifdef VULKAN_ENABLED - if (context_rd) { - return context_rd->get_vsync_mode(p_window_id); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window_id); } #endif // VULKAN_ENABLED @@ -1236,15 +1239,15 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win #ifdef RD_ENABLED #ifdef VULKAN_ENABLED if (p_rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextWayland); + rendering_context = memnew(RenderingContextDriverVulkanWayland); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Could not initialize %s", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Could not initialize %s", p_rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_CANT_CREATE; return; } @@ -1329,9 +1332,10 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win _show_window(); #ifdef RD_ENABLED - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -1351,8 +1355,12 @@ DisplayServerWayland::~DisplayServerWayland() { // TODO: Multiwindow support. if (main_window.visible) { #ifdef VULKAN_ENABLED - if (context_rd) { - context_rd->window_destroy(MAIN_WINDOW_ID); + if (rendering_device) { + rendering_device->screen_free(MAIN_WINDOW_ID); + } + + if (rendering_context) { + rendering_context->window_destroy(MAIN_WINDOW_ID); } #endif @@ -1374,12 +1382,11 @@ DisplayServerWayland::~DisplayServerWayland() { // Destroy all drivers. #ifdef RD_ENABLED if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); } - if (context_rd) { - memdelete(context_rd); + if (rendering_context) { + memdelete(rendering_context); } #endif diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index 3e7f3c4cb4a..58c5dab5867 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -39,7 +39,7 @@ #include "servers/rendering/rendering_device.h" #ifdef VULKAN_ENABLED -#include "wayland/vulkan_context_wayland.h" +#include "wayland/rendering_context_driver_vulkan_wayland.h" #endif #endif //RD_ENABLED @@ -123,7 +123,7 @@ class DisplayServerWayland : public DisplayServer { String rendering_driver; #ifdef RD_ENABLED - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif diff --git a/platform/linuxbsd/wayland/vulkan_context_wayland.cpp b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp similarity index 68% rename from platform/linuxbsd/wayland/vulkan_context_wayland.cpp rename to platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp index b3f28a16788..c874c45a8a6 100644 --- a/platform/linuxbsd/wayland/vulkan_context_wayland.cpp +++ b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_wayland.cpp */ +/* rendering_context_driver_vulkan_wayland.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,32 +28,43 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "vulkan_context_wayland.h" - #ifdef VULKAN_ENABLED +#include "rendering_context_driver_vulkan_wayland.h" + #ifdef USE_VOLK #include #else #include #endif -const char *VulkanContextWayland::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanWayland::_get_platform_surface_extension() const { return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; } -Error VulkanContextWayland::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanWayland::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkWaylandSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - createInfo.display = wpd->display; - createInfo.surface = wpd->surface; + VkWaylandSurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + create_info.display = wpd->display; + create_info.surface = wpd->surface; - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateWaylandSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateWaylandSurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); + + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); +} + +RenderingContextDriverVulkanWayland::RenderingContextDriverVulkanWayland() { + // Does nothing. +} + +RenderingContextDriverVulkanWayland::~RenderingContextDriverVulkanWayland() { + // Does nothing. } #endif // VULKAN_ENABLED diff --git a/platform/windows/vulkan_context_win.h b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.h similarity index 79% rename from platform/windows/vulkan_context_win.h rename to platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.h index 770a59de9e9..dfebca18903 100644 --- a/platform/windows/vulkan_context_win.h +++ b/platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_win.h */ +/* rendering_context_driver_vulkan_wayland.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,30 +28,30 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_WIN_H -#define VULKAN_CONTEXT_WIN_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" -#define WIN32_LEAN_AND_MEAN -#include - -class VulkanContextWindows : public VulkanContext { +class RenderingContextDriverVulkanWayland : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { - HWND window; - HINSTANCE instance; + struct wl_display *display; + struct wl_surface *surface; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextWindows(); - ~VulkanContextWindows(); + RenderingContextDriverVulkanWayland(); + ~RenderingContextDriverVulkanWayland(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_WIN_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_WAYLAND_H diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub index bbfaaf10d1c..75fe584ad58 100644 --- a/platform/linuxbsd/x11/SCsub +++ b/platform/linuxbsd/x11/SCsub @@ -21,7 +21,7 @@ if env["use_sowrap"]: ) if env["vulkan"]: - source_files.append("vulkan_context_x11.cpp") + source_files.append("rendering_context_driver_vulkan_x11.cpp") if env["opengl3"]: env.Append(CPPDEFINES=["GLAD_GLX_NO_X11"]) diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index c0937b6d4f4..b838e4b8705 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1670,7 +1670,11 @@ DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, V window_set_flag(WindowFlags(i), true, id); } } - +#ifdef RD_ENABLED + if (rendering_device) { + rendering_device->screen_create(id); + } +#endif return id; } @@ -1719,8 +1723,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { } #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_destroy(p_id); + if (rendering_device) { + rendering_device->screen_free(p_id); + } + + if (rendering_context) { + rendering_context->window_destroy(p_id); } #endif #ifdef GLES3_ENABLED @@ -2245,8 +2253,8 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { // Keep rendering context window size in sync #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(p_window, xwa.width, xwa.height); + if (rendering_context) { + rendering_context->window_set_size(p_window, xwa.width, xwa.height); } #endif #if defined(GLES3_ENABLED) @@ -3970,8 +3978,8 @@ void DisplayServerX11::_window_changed(XEvent *event) { wd.size = new_rect.size; #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(window_id, wd.size.width, wd.size.height); + if (rendering_context) { + rendering_context->window_set_size(window_id, wd.size.width, wd.size.height); } #endif #if defined(GLES3_ENABLED) @@ -5300,8 +5308,8 @@ void DisplayServerX11::set_icon(const Ref &p_icon) { void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif @@ -5318,8 +5326,8 @@ void DisplayServerX11::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mo DisplayServer::VSyncMode DisplayServerX11::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif #if defined(GLES3_ENABLED) @@ -5662,10 +5670,10 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V _update_size_hints(id); #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextX11::WindowPlatformData vulkan; + RenderingContextDriverVulkanX11::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -5674,8 +5682,11 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V wpd.vulkan.display = x11_display; } #endif - Error err = context_rd->window_create(id, p_vsync_mode, win_rect.size.width, win_rect.size.height, &wpd); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s window", context_rd->get_api_name())); + Error err = rendering_context->window_create(id, &wpd); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s window", rendering_driver)); + + rendering_context->window_set_size(id, win_rect.size.width, win_rect.size.height); + rendering_context->window_set_vsync_mode(id, p_vsync_mode); } #endif #ifdef GLES3_ENABLED @@ -6077,15 +6088,15 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode #if defined(RD_ENABLED) #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextX11); + rendering_context = memnew(RenderingContextDriverVulkanX11); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - ERR_PRINT(vformat("Could not initialize %s", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + ERR_PRINT(vformat("Could not initialize %s", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_CANT_CREATE; return; } @@ -6197,9 +6208,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode show_window(main_window); #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -6374,8 +6386,12 @@ DisplayServerX11::~DisplayServerX11() { //destroy all windows for (KeyValue &E : windows) { #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_destroy(E.key); + if (rendering_device) { + rendering_device->screen_free(E.key); + } + + if (rendering_context) { + rendering_context->window_destroy(E.key); } #endif #ifdef GLES3_ENABLED @@ -6419,14 +6435,13 @@ DisplayServerX11::~DisplayServerX11() { //destroy drivers #if defined(RD_ENABLED) if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); rendering_device = nullptr; } - if (context_rd) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + memdelete(rendering_context); + rendering_context = nullptr; } #endif diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index da4085772a6..7c094d6a41a 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -61,7 +61,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#include "x11/vulkan_context_x11.h" +#include "x11/rendering_context_driver_vulkan_x11.h" #endif #endif @@ -144,7 +144,7 @@ class DisplayServerX11 : public DisplayServer { GLManagerEGL_X11 *gl_manager_egl = nullptr; #endif #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif diff --git a/platform/linuxbsd/x11/vulkan_context_x11.cpp b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp similarity index 69% rename from platform/linuxbsd/x11/vulkan_context_x11.cpp rename to platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp index 3eee1706b06..bf44062266d 100644 --- a/platform/linuxbsd/x11/vulkan_context_x11.cpp +++ b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_x11.cpp */ +/* rendering_context_driver_vulkan_x11.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,7 +30,7 @@ #ifdef VULKAN_ENABLED -#include "vulkan_context_x11.h" +#include "rendering_context_driver_vulkan_x11.h" #ifdef USE_VOLK #include @@ -38,28 +38,33 @@ #include #endif -const char *VulkanContextX11::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanX11::_get_platform_surface_extension() const { return VK_KHR_XLIB_SURFACE_EXTENSION_NAME; } -Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanX11::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkXlibSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = wpd->display; - createInfo.window = wpd->window; + VkXlibSurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info.dpy = wpd->display; + create_info.window = wpd->window; - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateXlibSurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateXlibSurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); + + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } -VulkanContextX11::VulkanContextX11() { +RenderingContextDriverVulkanX11::RenderingContextDriverVulkanX11() { + // Does nothing. } -VulkanContextX11::~VulkanContextX11() { +RenderingContextDriverVulkanX11::~RenderingContextDriverVulkanX11() { + // Does nothing. } #endif // VULKAN_ENABLED diff --git a/platform/linuxbsd/x11/vulkan_context_x11.h b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.h similarity index 82% rename from platform/linuxbsd/x11/vulkan_context_x11.h rename to platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.h index 2390326b446..d525b69ec7b 100644 --- a/platform/linuxbsd/x11/vulkan_context_x11.h +++ b/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_x11.h */ +/* rendering_context_driver_vulkan_x11.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,29 +28,32 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_X11_H -#define VULKAN_CONTEXT_X11_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_X11_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_X11_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #include -class VulkanContextX11 : public VulkanContext { +class RenderingContextDriverVulkanX11 : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { ::Window window; Display *display; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextX11(); - ~VulkanContextX11(); + RenderingContextDriverVulkanX11(); + ~RenderingContextDriverVulkanX11(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_X11_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_X11_H diff --git a/platform/macos/SCsub b/platform/macos/SCsub index ad19f12c583..5a93c3a09ff 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -24,7 +24,7 @@ files = [ "dir_access_macos.mm", "tts_macos.mm", "joypad_macos.cpp", - "vulkan_context_macos.mm", + "rendering_context_driver_vulkan_macos.mm", "gl_manager_macos_angle.mm", "gl_manager_macos_legacy.mm", ] diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 24d4a349e22..e298b54970a 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -43,7 +43,7 @@ #include "servers/rendering/rendering_device.h" #if defined(VULKAN_ENABLED) -#include "vulkan_context_macos.h" +#include "rendering_context_driver_vulkan_macos.h" #endif // VULKAN_ENABLED #endif // RD_ENABLED @@ -137,7 +137,7 @@ private: GLManagerANGLE_MacOS *gl_manager_angle = nullptr; #endif #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif String rendering_driver; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index cad8435cbbb..ad8afaf46b2 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -195,10 +195,10 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod } #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextMacOS::WindowPlatformData vulkan; + RenderingContextDriverVulkanMacOS::WindowPlatformData vulkan; #endif } wpd; #ifdef VULKAN_ENABLED @@ -206,8 +206,11 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod wpd.vulkan.view_ptr = &wd.window_view; } #endif - Error err = context_rd->window_create(window_id_counter, p_vsync_mode, p_rect.size.width, p_rect.size.height, &wpd); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", context_rd->get_api_name())); + Error err = rendering_context->window_create(window_id_counter, &wpd); + ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", rendering_driver)); + + rendering_context->window_set_size(window_id_counter, p_rect.size.width, p_rect.size.height); + rendering_context->window_set_vsync_mode(window_id_counter, p_vsync_mode); } #endif #if defined(GLES3_ENABLED) @@ -257,11 +260,6 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod gl_manager_angle->window_resize(id, wd.size.width, wd.size.height); } #endif -#if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(id, wd.size.width, wd.size.height); - } -#endif return id; } @@ -791,8 +789,12 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) { } #endif #ifdef RD_ENABLED - if (context_rd) { - context_rd->window_destroy(p_window); + if (rendering_device) { + rendering_device->screen_free(p_window); + } + + if (rendering_context) { + rendering_context->window_destroy(p_window); } #endif windows.erase(p_window); @@ -800,6 +802,11 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) { } void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_height) { +#if defined(RD_ENABLED) + if (rendering_context) { + rendering_context->window_set_size(p_window, p_width, p_height); + } +#endif #if defined(GLES3_ENABLED) if (gl_manager_legacy) { gl_manager_legacy->window_resize(p_window, p_width, p_height); @@ -808,11 +815,6 @@ void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_hei gl_manager_angle->window_resize(p_window, p_width, p_height); } #endif -#if defined(VULKAN_ENABLED) - if (context_rd) { - context_rd->window_resize(p_window, p_width, p_height); - } -#endif } bool DisplayServerMacOS::has_feature(Feature p_feature) const { @@ -2850,7 +2852,11 @@ DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, window_set_flag(WindowFlags(i), true, id); } } - +#ifdef RD_ENABLED + if (rendering_device) { + rendering_device->screen_create(id); + } +#endif return id; } @@ -3811,8 +3817,8 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_ } #endif #if defined(VULKAN_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif } @@ -3828,8 +3834,8 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi } #endif #if defined(VULKAN_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif return DisplayServer::VSYNC_ENABLED; @@ -4632,16 +4638,16 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM #if defined(RD_ENABLED) #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextMacOS); + rendering_context = memnew(RenderingContextDriverVulkanMacOS); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_CANT_CREATE; - ERR_FAIL_MSG("Could not initialize Vulkan"); + ERR_FAIL_MSG("Could not initialize " + rendering_driver); } } #endif @@ -4676,9 +4682,10 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM } #endif #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -4714,14 +4721,13 @@ DisplayServerMacOS::~DisplayServerMacOS() { #endif #if defined(RD_ENABLED) if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); rendering_device = nullptr; } - if (context_rd) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + memdelete(rendering_context); + rendering_context = nullptr; } #endif diff --git a/platform/macos/vulkan_context_macos.h b/platform/macos/rendering_context_driver_vulkan_macos.h similarity index 82% rename from platform/macos/vulkan_context_macos.h rename to platform/macos/rendering_context_driver_vulkan_macos.h index 6205877120f..bbc67581dbd 100644 --- a/platform/macos/vulkan_context_macos.h +++ b/platform/macos/rendering_context_driver_vulkan_macos.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_macos.h */ +/* rendering_context_driver_vulkan_macos.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,28 +28,31 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_MACOS_H -#define VULKAN_CONTEXT_MACOS_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" #import -class VulkanContextMacOS : public VulkanContext { +class RenderingContextDriverVulkanMacOS : public RenderingContextDriverVulkan { +private: virtual const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { const id *view_ptr; }; - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; - VulkanContextMacOS(); - ~VulkanContextMacOS(); + RenderingContextDriverVulkanMacOS(); + ~RenderingContextDriverVulkanMacOS(); }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_MACOS_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_MACOS_H diff --git a/platform/macos/vulkan_context_macos.mm b/platform/macos/rendering_context_driver_vulkan_macos.mm similarity index 69% rename from platform/macos/vulkan_context_macos.mm rename to platform/macos/rendering_context_driver_vulkan_macos.mm index 18c3bda39b2..e0f8bf9e672 100644 --- a/platform/macos/vulkan_context_macos.mm +++ b/platform/macos/rendering_context_driver_vulkan_macos.mm @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_macos.mm */ +/* rendering_context_driver_vulkan_macos.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "vulkan_context_macos.h" +#include "rendering_context_driver_vulkan_macos.h" #ifdef VULKAN_ENABLED @@ -38,27 +38,32 @@ #include #endif -const char *VulkanContextMacOS::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanMacOS::_get_platform_surface_extension() const { return VK_MVK_MACOS_SURFACE_EXTENSION_NAME; } -Error VulkanContextMacOS::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanMacOS::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); - VkMacOSSurfaceCreateInfoMVK createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - createInfo.pView = (__bridge const void *)(*wpd->view_ptr); + VkMacOSSurfaceCreateInfoMVK create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + create_info.pView = (__bridge const void *)(*wpd->view_ptr); - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateMacOSSurfaceMVK(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateMacOSSurfaceMVK(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); + + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } -VulkanContextMacOS::VulkanContextMacOS() { +RenderingContextDriverVulkanMacOS::RenderingContextDriverVulkanMacOS() { + // Does nothing. } -VulkanContextMacOS::~VulkanContextMacOS() { +RenderingContextDriverVulkanMacOS::~RenderingContextDriverVulkanMacOS() { + // Does nothing. } #endif // VULKAN_ENABLED diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 6010d4ba767..37b6fa439c2 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -18,10 +18,10 @@ common_win = [ "joypad_windows.cpp", "tts_windows.cpp", "windows_terminal_logger.cpp", - "vulkan_context_win.cpp", "gl_manager_windows_native.cpp", "gl_manager_windows_angle.cpp", "wgl_detect_version.cpp", + "rendering_context_driver_vulkan_windows.cpp", ] common_win_wrap = [ diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e4550553243..80863441ce0 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -39,6 +39,12 @@ #include "main/main.h" #include "scene/resources/atlas_texture.h" +#if defined(VULKAN_ENABLED) +#include "rendering_context_driver_vulkan_windows.h" +#endif +#if defined(D3D12_ENABLED) +#include "drivers/d3d12/rendering_context_driver_d3d12.h" +#endif #if defined(GLES3_ENABLED) #include "drivers/gles3/rasterizer_gles3.h" #endif @@ -1290,6 +1296,11 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod if (mainwindow_icon) { SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon); } +#ifdef RD_ENABLED + if (rendering_device) { + rendering_device->screen_create(window_id); + } +#endif return window_id; } @@ -1344,8 +1355,12 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { } #ifdef RD_ENABLED - if (context_rd) { - context_rd->window_destroy(p_window); + if (rendering_device) { + rendering_device->screen_free(p_window); + } + + if (rendering_context) { + rendering_context->window_destroy(p_window); } #endif #ifdef GLES3_ENABLED @@ -1776,8 +1791,8 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo wd.height = h; #if defined(RD_ENABLED) - if (context_rd) { - context_rd->window_resize(p_window, w, h); + if (rendering_context) { + rendering_context->window_set_size(p_window, w, h); } #endif #if defined(GLES3_ENABLED) @@ -2830,8 +2845,8 @@ void DisplayServerWindows::set_icon(const Ref &p_icon) { void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - context_rd->set_vsync_mode(p_window, p_vsync_mode); + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); } #endif @@ -2848,8 +2863,8 @@ void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(RD_ENABLED) - if (context_rd) { - return context_rd->get_vsync_mode(p_window); + if (rendering_context) { + return rendering_context->window_get_vsync_mode(p_window); } #endif @@ -4054,9 +4069,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA rect_changed = true; } #if defined(RD_ENABLED) - if (context_rd && window.context_created) { + if (rendering_context && window.context_created) { // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed. - context_rd->window_resize(window_id, window.width, window.height); + rendering_context->window_set_size(window_id, window.width, window.height); } #endif } @@ -4612,13 +4627,13 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, } #ifdef RD_ENABLED - if (context_rd) { + if (rendering_context) { union { #ifdef VULKAN_ENABLED - VulkanContextWindows::WindowPlatformData vulkan; + RenderingContextDriverVulkanWindows::WindowPlatformData vulkan; #endif #ifdef D3D12_ENABLED - D3D12Context::WindowPlatformData d3d12; + RenderingContextDriverD3D12::WindowPlatformData d3d12; #endif } wpd; #ifdef VULKAN_ENABLED @@ -4632,13 +4647,16 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wpd.d3d12.window = wd.hWnd; } #endif - if (context_rd->window_create(id, p_vsync_mode, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, &wpd) != OK) { - ERR_PRINT(vformat("Failed to create %s Window.", context_rd->get_api_name())); - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context->window_create(id, &wpd) != OK) { + ERR_PRINT(vformat("Failed to create %s window.", rendering_driver)); + memdelete(rendering_context); + rendering_context = nullptr; windows.erase(id); return INVALID_WINDOW_ID; } + + rendering_context->window_set_size(id, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); + rendering_context->window_set_vsync_mode(id, p_vsync_mode); wd.context_created = true; } #endif @@ -4949,19 +4967,19 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win #if defined(RD_ENABLED) #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - context_rd = memnew(VulkanContextWindows); + rendering_context = memnew(RenderingContextDriverVulkanWindows); } #endif #if defined(D3D12_ENABLED) if (rendering_driver == "d3d12") { - context_rd = memnew(D3D12Context); + rendering_context = memnew(RenderingContextDriverD3D12); } #endif - if (context_rd) { - if (context_rd->initialize() != OK) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + if (rendering_context->initialize() != OK) { + memdelete(rendering_context); + rendering_context = nullptr; r_error = ERR_UNAVAILABLE; return; } @@ -5051,9 +5069,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win show_window(MAIN_WINDOW_ID); #if defined(RD_ENABLED) - if (context_rd) { + if (rendering_context) { rendering_device = memnew(RenderingDevice); - rendering_device->initialize(context_rd); + rendering_device->initialize(rendering_context, MAIN_WINDOW_ID); + rendering_device->screen_create(MAIN_WINDOW_ID); RendererCompositorRD::make_current(); } @@ -5165,8 +5184,12 @@ DisplayServerWindows::~DisplayServerWindows() { if (windows.has(MAIN_WINDOW_ID)) { #ifdef RD_ENABLED - if (context_rd) { - context_rd->window_destroy(MAIN_WINDOW_ID); + if (rendering_device) { + rendering_device->screen_free(MAIN_WINDOW_ID); + } + + if (rendering_context) { + rendering_context->window_destroy(MAIN_WINDOW_ID); } #endif if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) { @@ -5178,14 +5201,13 @@ DisplayServerWindows::~DisplayServerWindows() { #ifdef RD_ENABLED if (rendering_device) { - rendering_device->finalize(); memdelete(rendering_device); rendering_device = nullptr; } - if (context_rd) { - memdelete(context_rd); - context_rd = nullptr; + if (rendering_context) { + memdelete(rendering_context); + rendering_context = nullptr; } #endif diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 4e1d2cf85c0..91e7424de9d 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -54,13 +54,6 @@ #if defined(RD_ENABLED) #include "servers/rendering/rendering_device.h" - -#if defined(VULKAN_ENABLED) -#include "vulkan_context_win.h" -#endif -#if defined(D3D12_ENABLED) -#include "drivers/d3d12/d3d12_context.h" -#endif #endif #if defined(GLES3_ENABLED) @@ -349,7 +342,7 @@ class DisplayServerWindows : public DisplayServer { #endif #if defined(RD_ENABLED) - ApiContextRD *context_rd = nullptr; + RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 35b88acd268..056696ae2ff 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -48,13 +48,6 @@ #if defined(RD_ENABLED) #include "servers/rendering/rendering_device.h" - -#if defined(VULKAN_ENABLED) -#include "vulkan_context_win.h" -#endif -#if defined(D3D12_ENABLED) -#include "drivers/d3d12/d3d12_context.h" -#endif #endif #include diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/rendering_context_driver_vulkan_windows.cpp similarity index 71% rename from platform/windows/vulkan_context_win.cpp rename to platform/windows/rendering_context_driver_vulkan_windows.cpp index 69f8dc23fbf..f968ffc1d78 100644 --- a/platform/windows/vulkan_context_win.cpp +++ b/platform/windows/rendering_context_driver_vulkan_windows.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_win.cpp */ +/* rendering_context_driver_vulkan_windows.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,7 +30,9 @@ #if defined(WINDOWS_ENABLED) && defined(VULKAN_ENABLED) -#include "vulkan_context_win.h" +#include "core/os/os.h" + +#include "rendering_context_driver_vulkan_windows.h" #ifdef USE_VOLK #include @@ -38,32 +40,36 @@ #include #endif -const char *VulkanContextWindows::_get_platform_surface_extension() const { +const char *RenderingContextDriverVulkanWindows::_get_platform_surface_extension() const { return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; } -Error VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) { - const WindowPlatformData *wpd = (const WindowPlatformData *)p_platform_data; - - VkWin32SurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - createInfo.hinstance = wpd->instance; - createInfo.hwnd = wpd->window; - - VkSurfaceKHR surface = VK_NULL_HANDLE; - VkResult err = vkCreateWin32SurfaceKHR(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); -} - -VulkanContextWindows::VulkanContextWindows() { +RenderingContextDriverVulkanWindows::RenderingContextDriverVulkanWindows() { // Workaround for Vulkan not working on setups with AMD integrated graphics + NVIDIA dedicated GPU (GH-57708). // This prevents using AMD integrated graphics with Vulkan entirely, but it allows the engine to start // even on outdated/broken driver setups. OS::get_singleton()->set_environment("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1", "1"); } -VulkanContextWindows::~VulkanContextWindows() { +RenderingContextDriverVulkanWindows::~RenderingContextDriverVulkanWindows() { + // Does nothing. +} + +RenderingContextDriver::SurfaceID RenderingContextDriverVulkanWindows::surface_create(const void *p_platform_data) { + const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data); + + VkWin32SurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.hinstance = wpd->instance; + create_info.hwnd = wpd->window; + + VkSurfaceKHR vk_surface = VK_NULL_HANDLE; + VkResult err = vkCreateWin32SurfaceKHR(instance_get(), &create_info, nullptr, &vk_surface); + ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID()); + + Surface *surface = memnew(Surface); + surface->vk_surface = vk_surface; + return SurfaceID(surface); } #endif // WINDOWS_ENABLED && VULKAN_ENABLED diff --git a/platform/linuxbsd/wayland/vulkan_context_wayland.h b/platform/windows/rendering_context_driver_vulkan_windows.h similarity index 78% rename from platform/linuxbsd/wayland/vulkan_context_wayland.h rename to platform/windows/rendering_context_driver_vulkan_windows.h index b0a7d1ff87f..34546c9ea6c 100644 --- a/platform/linuxbsd/wayland/vulkan_context_wayland.h +++ b/platform/windows/rendering_context_driver_vulkan_windows.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* vulkan_context_wayland.h */ +/* rendering_context_driver_vulkan_windows.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,33 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef VULKAN_CONTEXT_WAYLAND_H -#define VULKAN_CONTEXT_WAYLAND_H +#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H +#define RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H #ifdef VULKAN_ENABLED -#include "drivers/vulkan/vulkan_context.h" +#include "drivers/vulkan/rendering_context_driver_vulkan.h" -class VulkanContextWayland : public VulkanContext { +#define WIN32_LEAN_AND_MEAN +#include + +class RenderingContextDriverVulkanWindows : public RenderingContextDriverVulkan { +private: const char *_get_platform_surface_extension() const override final; +protected: + SurfaceID surface_create(const void *p_platform_data) override final; + public: struct WindowPlatformData { - struct wl_display *display; - struct wl_surface *surface; + HWND window; + HINSTANCE instance; }; - Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) override final; + RenderingContextDriverVulkanWindows(); + ~RenderingContextDriverVulkanWindows() override final; }; #endif // VULKAN_ENABLED -#endif // VULKAN_CONTEXT_WAYLAND_H +#endif // RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h index 929c6610094..c61656bc77b 100644 --- a/servers/rendering/dummy/rasterizer_dummy.h +++ b/servers/rendering/dummy/rasterizer_dummy.h @@ -86,7 +86,6 @@ public: time += frame_step; } - void prepare_for_blitting_render_targets() override {} void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {} void end_viewport(bool p_swap_buffers) override {} diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 13767a38759..3c49e315160 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -96,7 +96,6 @@ public: virtual void initialize() = 0; virtual void begin_frame(double frame_step) = 0; - virtual void prepare_for_blitting_render_targets() = 0; virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; virtual void end_viewport(bool p_swap_buffers) = 0; diff --git a/servers/rendering/renderer_rd/api_context_rd.h b/servers/rendering/renderer_rd/api_context_rd.h deleted file mode 100644 index fd3be806052..00000000000 --- a/servers/rendering/renderer_rd/api_context_rd.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************/ -/* api_context_rd.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. */ -/**************************************************************************/ - -#ifndef API_CONTEXT_RD_H -#define API_CONTEXT_RD_H - -#include "servers/rendering/rendering_device.h" -#include "servers/rendering/rendering_device_driver.h" - -class ApiContextRD { -public: - virtual const char *get_api_name() const = 0; - virtual RenderingDevice::Capabilities get_device_capabilities() const = 0; - virtual const RDD::MultiviewCapabilities &get_multiview_capabilities() const = 0; - - virtual int get_swapchain_image_count() const = 0; - - virtual Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, int p_width, int p_height, const void *p_platform_data) = 0; - virtual void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) = 0; - virtual int window_get_width(DisplayServer::WindowID p_window = 0) = 0; - virtual int window_get_height(DisplayServer::WindowID p_window = 0) = 0; - virtual bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0) = 0; - virtual void window_destroy(DisplayServer::WindowID p_window_id) = 0; - virtual RDD::RenderPassID window_get_render_pass(DisplayServer::WindowID p_window = 0) = 0; - virtual RDD::FramebufferID window_get_framebuffer(DisplayServer::WindowID p_window = 0) = 0; - - virtual RID local_device_create() = 0; - virtual void local_device_push_command_buffers(RID p_local_device, const RDD::CommandBufferID *p_buffers, int p_count) = 0; - virtual void local_device_sync(RID p_local_device) = 0; - virtual void local_device_free(RID p_local_device) = 0; - - virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) = 0; - virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) = 0; - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) = 0; - virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) = 0; - virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) = 0; - virtual Error swap_buffers() = 0; - virtual Error initialize() = 0; - - virtual String get_device_vendor_name() const = 0; - virtual String get_device_name() const = 0; - virtual RDD::DeviceType get_device_type() const = 0; - virtual String get_device_api_version() const = 0; - virtual String get_device_pipeline_cache_uuid() const = 0; - - virtual void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) = 0; - virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const = 0; - - virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) = 0; - virtual bool is_debug_utils_enabled() const = 0; - - virtual ~ApiContextRD(); -}; - -#endif // API_CONTEXT_RD_H diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index a69877e6800..012451a5cd6 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -33,16 +33,16 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" -void RendererCompositorRD::prepare_for_blitting_render_targets() { - RD::get_singleton()->prepare_screen_for_drawing(); -} - void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) { - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen); - if (draw_list == RD::INVALID_ID) { - return; // Window is minimized and does not have valid swapchain, skip drawing without printing errors. + Error err = RD::get_singleton()->screen_prepare_for_drawing(p_screen); + if (err != OK) { + // Window is minimized and does not have valid swapchain, skip drawing without printing errors. + return; } + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen); + ERR_FAIL_COND(draw_list == RD::INVALID_ID); + for (int i = 0; i < p_amount; i++) { RID rd_texture = texture_storage->render_target_get_rd_texture(p_render_targets[i].render_target); ERR_CONTINUE(rd_texture.is_null()); @@ -122,7 +122,7 @@ void RendererCompositorRD::initialize() { blit.shader_version = blit.shader.version_create(); for (int i = 0; i < BLIT_MODE_MAX; i++) { - blit.pipelines[i] = RD::get_singleton()->render_pipeline_create(blit.shader.version_get_shader(blit.shader_version, i), RD::get_singleton()->screen_get_framebuffer_format(), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), i == BLIT_MODE_NORMAL_ALPHA ? RenderingDevice::PipelineColorBlendState::create_blend() : RenderingDevice::PipelineColorBlendState::create_disabled(), 0); + blit.pipelines[i] = RD::get_singleton()->render_pipeline_create(blit.shader.version_get_shader(blit.shader_version, i), RD::get_singleton()->screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID), RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), i == BLIT_MODE_NORMAL_ALPHA ? RenderingDevice::PipelineColorBlendState::create_blend() : RenderingDevice::PipelineColorBlendState::create_disabled(), 0); } //create index array for copy shader @@ -169,7 +169,7 @@ void RendererCompositorRD::set_boot_image(const Ref &p_image, const Color return; } - RD::get_singleton()->prepare_screen_for_drawing(); + RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID); RID texture = texture_storage->texture_allocate(); texture_storage->texture_2d_initialize(texture, p_image); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 9b073821c26..95c2b812d94 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -120,7 +120,6 @@ public: void initialize(); void begin_frame(double frame_step); - void prepare_for_blitting_render_targets(); void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); void end_viewport(bool p_swap_buffers) {} diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index db06fc4d21f..789c6c28455 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -36,7 +36,6 @@ #include "core/object/worker_thread_pool.h" #include "core/version.h" #include "renderer_compositor_rd.h" -#include "servers/rendering/renderer_rd/api_context_rd.h" #include "servers/rendering/rendering_device.h" #include "thirdparty/misc/smolv.h" @@ -398,7 +397,7 @@ static const uint32_t cache_file_version = 3; String ShaderRD::_get_cache_file_path(Version *p_version, int p_group) { const String &sha1 = _version_get_sha1(p_version); - const String &api_safe_name = String(RD::get_singleton()->get_context()->get_api_name()).validate_filename().to_lower(); + const String &api_safe_name = String(RD::get_singleton()->get_device_api_name()).validate_filename().to_lower(); const String &path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + "." + api_safe_name + ".cache"; return path; } diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 7a2415442d1..5bc5f333dfc 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -824,9 +824,6 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { RENDER_TIMESTAMP("< Render Viewports"); if (p_swap_buffers && !blit_to_screen_list.is_empty()) { - // This needs to be called to make screen swapping more efficient. - RSG::rasterizer->prepare_for_blitting_render_targets(); - for (const KeyValue> &E : blit_to_screen_list) { RSG::rasterizer->blit_render_targets_to_screen(E.key, E.value.ptr(), E.value.size()); } diff --git a/servers/rendering/rendering_context_driver.cpp b/servers/rendering/rendering_context_driver.cpp new file mode 100644 index 00000000000..19c0b0838c8 --- /dev/null +++ b/servers/rendering/rendering_context_driver.cpp @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* rendering_context_driver.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 "rendering_context_driver.h" + +RenderingContextDriver::~RenderingContextDriver() { +} + +RenderingContextDriver::SurfaceID RenderingContextDriver::surface_get_from_window(DisplayServer::WindowID p_window) const { + HashMap::ConstIterator it = window_surface_map.find(p_window); + if (it != window_surface_map.end()) { + return it->value; + } else { + return SurfaceID(); + } +} + +Error RenderingContextDriver::window_create(DisplayServer::WindowID p_window, const void *p_platform_data) { + SurfaceID surface = surface_create(p_platform_data); + if (surface != 0) { + window_surface_map[p_window] = surface; + return OK; + } else { + return ERR_CANT_CREATE; + } +} + +void RenderingContextDriver::window_set_size(DisplayServer::WindowID p_window, uint32_t p_width, uint32_t p_height) { + SurfaceID surface = surface_get_from_window(p_window); + if (surface) { + surface_set_size(surface, p_width, p_height); + } +} + +void RenderingContextDriver::window_set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_vsync_mode) { + SurfaceID surface = surface_get_from_window(p_window); + if (surface) { + surface_set_vsync_mode(surface, p_vsync_mode); + } +} + +DisplayServer::VSyncMode RenderingContextDriver::window_get_vsync_mode(DisplayServer::WindowID p_window) const { + SurfaceID surface = surface_get_from_window(p_window); + if (surface) { + return surface_get_vsync_mode(surface); + } else { + return DisplayServer::VSYNC_DISABLED; + } +} + +void RenderingContextDriver::window_destroy(DisplayServer::WindowID p_window) { + SurfaceID surface = surface_get_from_window(p_window); + if (surface) { + surface_destroy(surface); + } + + window_surface_map.erase(p_window); +} diff --git a/servers/rendering/rendering_context_driver.h b/servers/rendering/rendering_context_driver.h new file mode 100644 index 00000000000..df1424da951 --- /dev/null +++ b/servers/rendering/rendering_context_driver.h @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* rendering_context_driver.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. */ +/**************************************************************************/ + +#ifndef RENDERING_CONTEXT_DRIVER_H +#define RENDERING_CONTEXT_DRIVER_H + +#include "core/object/object.h" +#include "servers/display_server.h" + +class RenderingDeviceDriver; + +class RenderingContextDriver { +public: + typedef uint64_t SurfaceID; + +private: + HashMap window_surface_map; + +public: + SurfaceID surface_get_from_window(DisplayServer::WindowID p_window) const; + Error window_create(DisplayServer::WindowID p_window, const void *p_platform_data); + void window_set_size(DisplayServer::WindowID p_window, uint32_t p_width, uint32_t p_height); + void window_set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_vsync_mode); + DisplayServer::VSyncMode window_get_vsync_mode(DisplayServer::WindowID p_window) const; + void window_destroy(DisplayServer::WindowID p_window); + +public: + enum Vendor { + VENDOR_UNKNOWN = 0x0, + VENDOR_AMD = 0x1002, + VENDOR_IMGTEC = 0x1010, + VENDOR_APPLE = 0x106B, + VENDOR_NVIDIA = 0x10DE, + VENDOR_ARM = 0x13B5, + VENDOR_MICROSOFT = 0x1414, + VENDOR_QUALCOMM = 0x5143, + VENDOR_INTEL = 0x8086 + }; + + enum DeviceType { + DEVICE_TYPE_OTHER = 0x0, + DEVICE_TYPE_INTEGRATED_GPU = 0x1, + DEVICE_TYPE_DISCRETE_GPU = 0x2, + DEVICE_TYPE_VIRTUAL_GPU = 0x3, + DEVICE_TYPE_CPU = 0x4, + DEVICE_TYPE_MAX = 0x5 + }; + + struct Device { + String name = "Unknown"; + Vendor vendor = VENDOR_UNKNOWN; + DeviceType type = DEVICE_TYPE_OTHER; + }; + + virtual ~RenderingContextDriver(); + virtual Error initialize() = 0; + virtual const Device &device_get(uint32_t p_device_index) const = 0; + virtual uint32_t device_get_count() const = 0; + virtual bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const = 0; + virtual RenderingDeviceDriver *driver_create() = 0; + virtual void driver_free(RenderingDeviceDriver *p_driver) = 0; + virtual SurfaceID surface_create(const void *p_platform_data) = 0; + virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) = 0; + virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) = 0; + virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const = 0; + virtual uint32_t surface_get_width(SurfaceID p_surface) const = 0; + virtual uint32_t surface_get_height(SurfaceID p_surface) const = 0; + virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) = 0; + virtual bool surface_get_needs_resize(SurfaceID p_surface) const = 0; + virtual void surface_destroy(SurfaceID p_surface) = 0; + virtual bool is_debug_utils_enabled() const = 0; +}; + +#endif // RENDERING_CONTEXT_DRIVER_H diff --git a/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc index edc08e972de..ee9481280aa 100644 --- a/servers/rendering/rendering_device.compat.inc +++ b/servers/rendering/rendering_device.compat.inc @@ -117,6 +117,10 @@ Error RenderingDevice::_texture_resolve_multisample_bind_compat_84976(RID p_from return texture_resolve_multisample(p_from_texture, p_to_texture); } +RenderingDevice::FramebufferFormatID RenderingDevice::_screen_get_framebuffer_format_bind_compat_87340() const { + return screen_get_framebuffer_format(DisplayServer::MAIN_WINDOW_ID); +} + void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::_shader_create_from_bytecode_bind_compat_79606); ClassDB::bind_compatibility_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::_draw_list_end_bind_compat_81356, DEFVAL(7)); @@ -132,6 +136,7 @@ void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer", "post_barrier"), &RenderingDevice::_texture_copy_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::_texture_clear_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::_texture_resolve_multisample_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("screen_get_framebuffer_format"), &RenderingDevice::_screen_get_framebuffer_format_bind_compat_87340); } #endif diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 5d65118159d..3290738b408 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -35,7 +35,71 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" -#include "servers/rendering/renderer_rd/api_context_rd.h" + +#define FORCE_SEPARATE_PRESENT_QUEUE 0 + +/**************************/ +/**** HELPER FUNCTIONS ****/ +/**************************/ + +static String _get_device_vendor_name(const RenderingContextDriver::Device &p_device) { + switch (p_device.vendor) { + case RenderingContextDriver::VENDOR_AMD: + return "AMD"; + case RenderingContextDriver::VENDOR_IMGTEC: + return "ImgTec"; + case RenderingContextDriver::VENDOR_APPLE: + return "Apple"; + case RenderingContextDriver::VENDOR_NVIDIA: + return "NVIDIA"; + case RenderingContextDriver::VENDOR_ARM: + return "ARM"; + case RenderingContextDriver::VENDOR_MICROSOFT: + return "Microsoft"; + case RenderingContextDriver::VENDOR_QUALCOMM: + return "Qualcomm"; + case RenderingContextDriver::VENDOR_INTEL: + return "Intel"; + default: + return "Unknown"; + } +} + +static String _get_device_type_name(const RenderingContextDriver::Device &p_device) { + switch (p_device.type) { + case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: + return "Integrated"; + case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: + return "Discrete"; + case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: + return "Virtual"; + case RenderingContextDriver::DEVICE_TYPE_CPU: + return "CPU"; + case RenderingContextDriver::DEVICE_TYPE_OTHER: + default: + return "Other"; + } +} + +static uint32_t _get_device_type_score(const RenderingContextDriver::Device &p_device) { + switch (p_device.type) { + case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: + return 4; + case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: + return 5; + case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: + return 3; + case RenderingContextDriver::DEVICE_TYPE_CPU: + return 2; + case RenderingContextDriver::DEVICE_TYPE_OTHER: + default: + return 1; + } +} + +/**************************/ +/**** RENDERING DEVICE ****/ +/**************************/ // When true, the command graph will attempt to reorder the rendering commands submitted by the user based on the dependencies detected from // the commands automatically. This should improve rendering performance in most scenarios at the cost of some extra CPU overhead. @@ -240,7 +304,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re // and this frame is not even done. // If this is the main thread, it means the user is likely loading a lot of resources at once,. // Otherwise, the thread should just be blocked until the next frame (currently unimplemented). - r_required_action = STAGING_REQUIRED_ACTION_FLUSH_CURRENT; + r_required_action = STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL; } } else { @@ -249,7 +313,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re } } - } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frame_count) { + } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frames.size()) { // This is an old block, which was already processed, let's reuse. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; @@ -268,7 +332,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re // Let's flush older frames. // The logic here is that if a game is loading a lot of data from the main thread, it will need to be stalled anyway. // If loading from a separate thread, we can block that thread until next frame when more room is made (not currently implemented, though). - r_required_action = STAGING_REQUIRED_ACTION_FLUSH_OLDER; + r_required_action = STAGING_REQUIRED_ACTION_STALL_PREVIOUS; } } @@ -286,9 +350,8 @@ void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAct case STAGING_REQUIRED_ACTION_NONE: { // Do nothing. } break; - case STAGING_REQUIRED_ACTION_FLUSH_CURRENT: { - // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. - _flush(true); + case STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL: { + _flush_and_stall_for_all_frames(); // Clear the whole staging buffer. for (int i = 0; i < staging_buffer_blocks.size(); i++) { @@ -299,8 +362,8 @@ void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAct // Claim for current frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } break; - case STAGING_REQUIRED_ACTION_FLUSH_OLDER: { - _flush(false); + case STAGING_REQUIRED_ACTION_STALL_PREVIOUS: { + _stall_for_previous_frames(); for (int i = 0; i < staging_buffer_blocks.size(); i++) { // Clear all blocks but the ones from this frame. @@ -340,7 +403,7 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t return err; } - if (p_use_draw_queue && !command_buffer_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_CURRENT) { + if (p_use_draw_queue && !command_buffer_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) { if (_buffer_make_mutable(p_buffer, p_buffer_id)) { // The buffer must be mutable to be used as a copy destination. draw_graph.add_synchronization(); @@ -507,7 +570,7 @@ Vector RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset draw_graph.add_buffer_get_data(buffer->driver_id, buffer->draw_tracker, tmp_buffer, region); // Flush everything so memory can be safely mapped. - _flush(true); + _flush_and_stall_for_all_frames(); uint8_t *buffer_mem = driver->buffer_map(tmp_buffer); ERR_FAIL_NULL_V(buffer_mem, Vector()); @@ -1078,7 +1141,7 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, required_action, false); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_CURRENT) { + if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) { if (_texture_make_mutable(texture, p_texture)) { // The texture must be mutable to be used as a copy destination. draw_graph.add_synchronization(); @@ -1314,7 +1377,8 @@ Vector RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye draw_graph.add_texture_get_data(tex->driver_id, tex->draw_tracker, tmp_buffer, command_buffer_texture_copy_regions_vector); - _flush(true); + // Flush everything so memory can be safely mapped. + _flush_and_stall_for_all_frames(); const uint8_t *read_ptr = driver->buffer_map(tmp_buffer); ERR_FAIL_NULL_V(read_ptr, Vector()); @@ -2976,7 +3040,7 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_ p_specialization_constants); ERR_FAIL_COND_V(!pipeline.driver_id, RID()); - if (pipelines_cache_enabled) { + if (pipeline_cache_enabled) { _update_pipeline_cache(); } @@ -3053,7 +3117,7 @@ RID RenderingDevice::compute_pipeline_create(RID p_shader, const Vectorcompute_pipeline_create(shader->driver_id, p_specialization_constants); ERR_FAIL_COND_V(!pipeline.driver_id, RID()); - if (pipelines_cache_enabled) { + if (pipeline_cache_enabled) { _update_pipeline_cache(); } @@ -3084,23 +3148,95 @@ bool RenderingDevice::compute_pipeline_is_valid(RID p_pipeline) { /**** SCREEN ****/ /****************/ +uint32_t RenderingDevice::_get_swap_chain_desired_count() const { + return MAX(2U, uint32_t(GLOBAL_GET("rendering/rendering_device/vsync/swapchain_image_count"))); +} + +Error RenderingDevice::screen_create(DisplayServer::WindowID p_screen) { + _THREAD_SAFE_METHOD_ + + RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen); + ERR_FAIL_COND_V_MSG(surface == 0, ERR_CANT_CREATE, "A surface was not created for the screen."); + + HashMap::ConstIterator it = screen_swap_chains.find(p_screen); + ERR_FAIL_COND_V_MSG(it != screen_swap_chains.end(), ERR_CANT_CREATE, "A swap chain was already created for the screen."); + + RDD::SwapChainID swap_chain = driver->swap_chain_create(surface); + ERR_FAIL_COND_V_MSG(swap_chain.id == 0, ERR_CANT_CREATE, "Unable to create swap chain."); + + Error err = driver->swap_chain_resize(main_queue, swap_chain, _get_swap_chain_desired_count()); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Unable to resize the new swap chain."); + + screen_swap_chains[p_screen] = swap_chain; + + return OK; +} + +Error RenderingDevice::screen_prepare_for_drawing(DisplayServer::WindowID p_screen) { + _THREAD_SAFE_METHOD_ + + HashMap::ConstIterator it = screen_swap_chains.find(p_screen); + ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), ERR_CANT_CREATE, "A swap chain was not created for the screen."); + + // Erase the framebuffer corresponding to this screen from the map in case any of the operations fail. + screen_framebuffers.erase(p_screen); + + // If this frame has already queued this swap chain for presentation, we present it and remove it from the pending list. + uint32_t to_present_index = 0; + while (to_present_index < frames[frame].swap_chains_to_present.size()) { + if (frames[frame].swap_chains_to_present[to_present_index] == it->value) { + driver->command_queue_present(present_queue, it->value, {}); + frames[frame].swap_chains_to_present.remove_at(to_present_index); + } else { + to_present_index++; + } + } + + bool resize_required = false; + RDD::FramebufferID framebuffer = driver->swap_chain_acquire_framebuffer(main_queue, it->value, resize_required); + if (resize_required) { + // Flush everything so nothing can be using the swap chain before resizing it. + _flush_and_stall_for_all_frames(); + + Error err = driver->swap_chain_resize(main_queue, it->value, _get_swap_chain_desired_count()); + if (err != OK) { + // Resize is allowed to fail silently because the window can be minimized. + return err; + } + + framebuffer = driver->swap_chain_acquire_framebuffer(main_queue, it->value, resize_required); + } + + ERR_FAIL_COND_V_MSG(framebuffer.id == 0, FAILED, "Unable to acquire framebuffer."); + + // Store the framebuffer that will be used next to draw to this screen. + screen_framebuffers[p_screen] = framebuffer; + frames[frame].swap_chains_to_present.push_back(it->value); + + return OK; +} + int RenderingDevice::screen_get_width(DisplayServer::WindowID p_screen) const { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), -1, "Local devices have no screen"); - return context->window_get_width(p_screen); + RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen); + ERR_FAIL_COND_V_MSG(surface == 0, 0, "A surface was not created for the screen."); + return context->surface_get_width(surface); } int RenderingDevice::screen_get_height(DisplayServer::WindowID p_screen) const { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), -1, "Local devices have no screen"); - return context->window_get_height(p_screen); + RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen); + ERR_FAIL_COND_V_MSG(surface == 0, 0, "A surface was not created for the screen."); + return context->surface_get_height(surface); } -RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_format() const { +RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_format(DisplayServer::WindowID p_screen) const { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); - DataFormat format = driver->screen_get_format(); + HashMap::ConstIterator it = screen_swap_chains.find(p_screen); + ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), FAILED, "Screen was never prepared."); + + DataFormat format = driver->swap_chain_get_format(it->value); ERR_FAIL_COND_V(format == DATA_FORMAT_MAX, INVALID_ID); AttachmentFormat attachment; @@ -3112,33 +3248,54 @@ RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_for return const_cast(this)->framebuffer_format_create(screen_attachment); } +Error RenderingDevice::screen_free(DisplayServer::WindowID p_screen) { + _THREAD_SAFE_METHOD_ + + HashMap::ConstIterator it = screen_swap_chains.find(p_screen); + ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), FAILED, "Screen was never created."); + + // Flush everything so nothing can be using the swap chain before erasing it. + _flush_and_stall_for_all_frames(); + + const DisplayServer::WindowID screen = it->key; + const RDD::SwapChainID swap_chain = it->value; + driver->swap_chain_free(swap_chain); + screen_framebuffers.erase(screen); + screen_swap_chains.erase(screen); + + return OK; +} + /*******************/ /**** DRAW LIST ****/ /*******************/ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayServer::WindowID p_screen, const Color &p_clear_color) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); - if (!context->window_is_valid_swapchain(p_screen)) { - return INVALID_ID; - } + RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen); + HashMap::ConstIterator sc_it = screen_swap_chains.find(p_screen); + HashMap::ConstIterator fb_it = screen_framebuffers.find(p_screen); + ERR_FAIL_COND_V_MSG(surface == 0, 0, "A surface was not created for the screen."); + ERR_FAIL_COND_V_MSG(sc_it == screen_swap_chains.end(), INVALID_ID, "Screen was never prepared."); + ERR_FAIL_COND_V_MSG(fb_it == screen_framebuffers.end(), INVALID_ID, "Framebuffer was never prepared."); - Rect2i viewport = Rect2i(0, 0, context->window_get_width(p_screen), context->window_get_height(p_screen)); + Rect2i viewport = Rect2i(0, 0, context->surface_get_width(surface), context->surface_get_height(surface)); _draw_list_allocate(viewport, 0); #ifdef DEBUG_ENABLED - draw_list_framebuffer_format = screen_get_framebuffer_format(); + draw_list_framebuffer_format = screen_get_framebuffer_format(p_screen); #endif draw_list_subpass_count = 1; RDD::RenderPassClearValue clear_value; clear_value.color = p_clear_color; - draw_graph.add_draw_list_begin(context->window_get_render_pass(p_screen), context->window_get_framebuffer(p_screen), viewport, clear_value, true, false); + RDD::RenderPassID render_pass = driver->swap_chain_get_render_pass(sc_it->value); + draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, clear_value, true, false); _draw_list_set_viewport(viewport); _draw_list_set_scissor(viewport); @@ -3775,7 +3932,6 @@ Error RenderingDevice::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_ draw_list = memnew(DrawList); draw_list->viewport = p_viewport; - draw_list_count = 0; return OK; } @@ -4533,130 +4689,50 @@ void RenderingDevice::draw_command_end_label() { } String RenderingDevice::get_device_vendor_name() const { - return context->get_device_vendor_name(); + return _get_device_vendor_name(device); } String RenderingDevice::get_device_name() const { - return context->get_device_name(); + return device.name; } RenderingDevice::DeviceType RenderingDevice::get_device_type() const { - return context->get_device_type(); + return DeviceType(device.type); +} + +String RenderingDevice::get_device_api_name() const { + return driver->get_api_name(); } String RenderingDevice::get_device_api_version() const { - return context->get_device_api_version(); + return driver->get_api_version(); } String RenderingDevice::get_device_pipeline_cache_uuid() const { - return context->get_device_pipeline_cache_uuid(); -} - -void RenderingDevice::_finalize_command_buffers(bool p_postpare) { - if (draw_list) { - ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); - } - - if (compute_list) { - ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); - } - - { // Complete the setup buffer (that needs to be processed before anything else). - draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); - - if (p_postpare) { - context->postpare_buffers(frames[frame].draw_command_buffer); - } - - driver->end_segment(); - driver->command_buffer_end(frames[frame].setup_command_buffer); - driver->command_buffer_end(frames[frame].draw_command_buffer); - } -} - -void RenderingDevice::_begin_frame() { - draw_graph.begin(); - - // Erase pending resources. - _free_pending_resources(frame); - - // Create setup command buffer and set as the setup buffer. - - { - bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); - ERR_FAIL_COND(!ok); - ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); - ERR_FAIL_COND(!ok); - - if (local_device.is_null()) { - context->append_command_buffer(frames[frame].draw_command_buffer); - context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. - } - - driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); - } - - // Advance current frame. - frames_drawn++; - // Advance staging buffer if used. - if (staging_buffer_used) { - staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); - staging_buffer_used = false; - } - - if (frames[frame].timestamp_count) { - driver->timestamp_query_pool_get_results(frames[frame].timestamp_pool, frames[frame].timestamp_count, frames[frame].timestamp_result_values.ptr()); - driver->command_timestamp_query_pool_reset(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count); - SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); - SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); - } - - frames[frame].timestamp_result_count = frames[frame].timestamp_count; - frames[frame].timestamp_count = 0; - frames[frame].index = Engine::get_singleton()->get_frames_drawn(); + return driver->get_pipeline_cache_uuid(); } void RenderingDevice::swap_buffers() { - ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); _THREAD_SAFE_METHOD_ - _finalize_command_buffers(true); - - // Swap buffers. - if (!screen_prepared) { - context->flush(true, true, false); - } else { - screen_prepared = false; - context->swap_buffers(); - } - - frame = (frame + 1) % frame_count; + _end_frame(); + _execute_frame(true); + _present_frame(); + // Advance to the next frame and begin recording again. + frame = (frame + 1) % frames.size(); _begin_frame(); } void RenderingDevice::submit() { _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync."); - ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done."); - - _finalize_command_buffers(false); - - RDD::CommandBufferID command_buffers[2] = { frames[frame].setup_command_buffer, frames[frame].draw_command_buffer }; - context->local_device_push_command_buffers(local_device, command_buffers, 2); - local_device_processing = true; + _end_frame(); + _execute_frame(false); } void RenderingDevice::sync() { _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync."); - ERR_FAIL_COND_MSG(!local_device_processing, "sync can only be called after a submit"); - - context->local_device_sync(local_device); _begin_frame(); - local_device_processing = false; } void RenderingDevice::_free_pending_resources(int p_frame) { @@ -4741,14 +4817,8 @@ void RenderingDevice::_free_pending_resources(int p_frame) { } } -void RenderingDevice::prepare_screen_for_drawing() { - _THREAD_SAFE_METHOD_ - context->prepare_buffers(frames[frame].draw_command_buffer); - screen_prepared = true; -} - uint32_t RenderingDevice::get_frame_delay() const { - return frame_count; + return frames.size(); } uint64_t RenderingDevice::get_memory_usage(MemoryType p_type) const { @@ -4769,113 +4839,239 @@ uint64_t RenderingDevice::get_memory_usage(MemoryType p_type) const { } } -void RenderingDevice::_flush(bool p_current_frame) { - if (local_device.is_valid() && !p_current_frame) { - return; // Flushing previous frames has no effect with local device. +void RenderingDevice::_begin_frame() { + // Before beginning this frame, wait on the fence if it was signaled to make sure its work is finished. + if (frames[frame].draw_fence_signaled) { + driver->fence_wait(frames[frame].draw_fence); + frames[frame].draw_fence_signaled = false; } - // Not doing this crashes RADV (undefined behavior). - if (p_current_frame) { - draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); - driver->end_segment(); - driver->command_buffer_end(frames[frame].setup_command_buffer); - driver->command_buffer_end(frames[frame].draw_command_buffer); - draw_graph.begin(); + // Begin recording on the frame's command buffers. + driver->begin_segment(frame, frames_drawn++); + driver->command_buffer_begin(frames[frame].setup_command_buffer); + driver->command_buffer_begin(frames[frame].draw_command_buffer); + + // Reset the graph. + draw_graph.begin(); + + // Erase pending resources. + _free_pending_resources(frame); + + // Advance staging buffer if used. + if (staging_buffer_used) { + staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); + staging_buffer_used = false; } - if (local_device.is_valid()) { - RDD::CommandBufferID command_buffers[2] = { frames[frame].setup_command_buffer, frames[frame].draw_command_buffer }; - context->local_device_push_command_buffers(local_device, command_buffers, 2); - context->local_device_sync(local_device); + if (frames[frame].timestamp_count) { + driver->timestamp_query_pool_get_results(frames[frame].timestamp_pool, frames[frame].timestamp_count, frames[frame].timestamp_result_values.ptr()); + driver->command_timestamp_query_pool_reset(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count); + SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); + SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); + } - bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); - ERR_FAIL_COND(!ok); - ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); - ERR_FAIL_COND(!ok); + frames[frame].timestamp_result_count = frames[frame].timestamp_count; + frames[frame].timestamp_count = 0; + frames[frame].index = Engine::get_singleton()->get_frames_drawn(); +} - driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); - } else { - context->flush(p_current_frame, p_current_frame); - // Re-create the setup command. - if (p_current_frame) { - bool ok = driver->command_buffer_begin(frames[frame].setup_command_buffer); - ERR_FAIL_COND(!ok); +void RenderingDevice::_end_frame() { + if (draw_list) { + ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); + } - context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. - ok = driver->command_buffer_begin(frames[frame].draw_command_buffer); - ERR_FAIL_COND(!ok); - context->append_command_buffer(frames[frame].draw_command_buffer); + if (compute_list) { + ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); + } - driver->begin_segment(frames[frame].draw_command_buffer, frame, frames_drawn); + draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); + driver->command_buffer_end(frames[frame].setup_command_buffer); + driver->command_buffer_end(frames[frame].draw_command_buffer); + driver->end_segment(); +} + +void RenderingDevice::_execute_frame(bool p_signal_for_present) { + const bool frame_can_present = !frames[frame].swap_chains_to_present.is_empty(); + const VectorView execute_draw_semaphore = p_signal_for_present && frame_can_present ? frames[frame].draw_semaphore : VectorView(); + driver->command_queue_execute(main_queue, frames[frame].setup_command_buffer, {}, frames[frame].setup_semaphore, {}); + driver->command_queue_execute(main_queue, frames[frame].draw_command_buffer, frames[frame].setup_semaphore, execute_draw_semaphore, frames[frame].draw_fence); + frames[frame].draw_fence_signaled = true; +} + +void RenderingDevice::_present_frame() { + if (!frames[frame].swap_chains_to_present.is_empty()) { + driver->command_queue_present(present_queue, frames[frame].swap_chains_to_present, frames[frame].draw_semaphore); + frames[frame].swap_chains_to_present.clear(); + } +} + +void RenderingDevice::_stall_for_previous_frames() { + for (uint32_t i = 0; i < frames.size(); i++) { + if (frames[i].draw_fence_signaled) { + driver->fence_wait(frames[i].draw_fence); + frames[i].draw_fence_signaled = false; } } } -void RenderingDevice::initialize(ApiContextRD *p_context, bool p_local_device) { - context = p_context; +void RenderingDevice::_flush_and_stall_for_all_frames() { + _stall_for_previous_frames(); + _end_frame(); + _execute_frame(false); + _begin_frame(); +} - device_capabilities = p_context->get_device_capabilities(); +Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServer::WindowID p_main_window) { + Error err; - if (p_local_device) { - frame_count = 1; - local_device = context->local_device_create(); - } else { - frame_count = context->get_swapchain_image_count() + 1; // Always need one extra to ensure it's unused at any time, without having to use a fence for this. + RenderingContextDriver::SurfaceID main_surface = 0; + const bool main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID); + if (p_main_window != DisplayServer::INVALID_WINDOW_ID) { + // Retrieve the surface from the main window if it was specified. + main_surface = p_context->surface_get_from_window(p_main_window); + ERR_FAIL_COND_V(main_surface == 0, FAILED); } - driver = context->get_driver(local_device); + + context = p_context; + driver = context->driver_create(); + + print_verbose("Devices:"); + int32_t device_index = Engine::get_singleton()->get_gpu_index(); + const uint32_t device_count = context->device_get_count(); + const bool detect_device = (device_index < 0) || (device_index >= int32_t(device_count)); + uint32_t device_type_score = 0; + for (uint32_t i = 0; i < device_count; i++) { + RenderingContextDriver::Device device_option = context->device_get(i); + String name = device_option.name; + String vendor = _get_device_vendor_name(device_option); + String type = _get_device_type_name(device_option); + bool present_supported = main_surface != 0 ? context->device_supports_present(i, main_surface) : false; + print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + type); + if (detect_device && (present_supported || main_surface == 0)) { + // If a window was specified, present must be supported by the device to be available as an option. + // Assign a score for each type of device and prefer the device with the higher score. + uint32_t option_score = _get_device_type_score(device_option); + if (option_score > device_type_score) { + device_index = i; + device_type_score = option_score; + } + } + } + + ERR_FAIL_COND_V_MSG((device_index < 0) || (device_index >= int32_t(device_count)), ERR_CANT_CREATE, "None of the devices supports both graphics and present queues."); + + uint32_t frame_count = 1; + if (main_surface != 0) { + frame_count = MAX(2U, uint32_t(GLOBAL_GET("rendering/rendering_device/vsync/frame_queue_size"))); + } + + frame = 0; + frames.resize(frame_count); max_timestamp_query_elements = 256; - frames.resize(frame_count); - frame = 0; - // Create setup and frame buffers. - for (int i = 0; i < frame_count; i++) { + device = context->device_get(device_index); + err = driver->initialize(device_index, frame_count); + ERR_FAIL_COND_V_MSG(err != OK, FAILED, "Failed to initialize driver for device."); + + if (main_instance) { + // Only the singleton instance with a display should print this information. + String rendering_method; + if (OS::get_singleton()->get_current_rendering_method() == "mobile") { + rendering_method = "Forward Mobile"; + } else { + rendering_method = "Forward+"; + } + + // Output our device version. + print_line(vformat("%s %s - %s - Using Device #%d: %s - %s", get_device_api_name(), get_device_api_version(), rendering_method, device_index, _get_device_vendor_name(device), device.name)); + } + + // Pick the main queue family. It is worth noting we explicitly do not request the transfer bit, as apparently the specification defines + // that the existence of either the graphics or compute bit implies that the queue can also do transfer operations, but it is optional + // to indicate whether it supports them or not with the dedicated transfer bit if either is set. + BitField main_queue_bits; + main_queue_bits.set_flag(RDD::COMMAND_QUEUE_FAMILY_GRAPHICS_BIT); + main_queue_bits.set_flag(RDD::COMMAND_QUEUE_FAMILY_COMPUTE_BIT); + +#if !FORCE_SEPARATE_PRESENT_QUEUE + // Needing to use a separate queue for presentation is an edge case that remains to be seen what hardware triggers it at all. + main_queue_family = driver->command_queue_family_get(main_queue_bits, main_surface); + if (!main_queue_family && (main_surface != 0)) +#endif + { + // If it was not possible to find a main queue that supports the surface, we attempt to get two different queues instead. + main_queue_family = driver->command_queue_family_get(main_queue_bits); + present_queue_family = driver->command_queue_family_get(BitField(), main_surface); + ERR_FAIL_COND_V(!present_queue_family, FAILED); + } + + ERR_FAIL_COND_V(!main_queue_family, FAILED); + + // Create the main queue. + main_queue = driver->command_queue_create(main_queue_family, true); + ERR_FAIL_COND_V(!main_queue, FAILED); + + if (present_queue_family) { + // Create the presentation queue. + present_queue = driver->command_queue_create(present_queue_family); + ERR_FAIL_COND_V(!present_queue, FAILED); + } else { + present_queue = main_queue; + } + + // Create data for all the frames. + for (uint32_t i = 0; i < frames.size(); i++) { frames[i].index = 0; - // Create command pool, one per frame is recommended. - frames[i].command_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY); - ERR_FAIL_COND(!frames[i].command_pool); + // Create command pool, command buffers, semaphores and fences. + frames[i].command_pool = driver->command_pool_create(main_queue_family, RDD::COMMAND_BUFFER_TYPE_PRIMARY); + ERR_FAIL_COND_V(!frames[i].command_pool, FAILED); + frames[i].setup_command_buffer = driver->command_buffer_create(frames[i].command_pool); + ERR_FAIL_COND_V(!frames[i].setup_command_buffer, FAILED); + frames[i].draw_command_buffer = driver->command_buffer_create(frames[i].command_pool); + ERR_FAIL_COND_V(!frames[i].draw_command_buffer, FAILED); + frames[i].setup_semaphore = driver->semaphore_create(); + ERR_FAIL_COND_V(!frames[i].setup_semaphore, FAILED); + frames[i].draw_semaphore = driver->semaphore_create(); + ERR_FAIL_COND_V(!frames[i].draw_semaphore, FAILED); + frames[i].draw_fence = driver->fence_create(); + ERR_FAIL_COND_V(!frames[i].draw_fence, FAILED); + frames[i].draw_fence_signaled = false; - // Create command buffers. - frames[i].setup_command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY, frames[i].command_pool); - ERR_CONTINUE(!frames[i].setup_command_buffer); - frames[i].draw_command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_PRIMARY, frames[i].command_pool); - ERR_CONTINUE(!frames[i].draw_command_buffer); - - { - // Create query pool. - frames[i].timestamp_pool = driver->timestamp_query_pool_create(max_timestamp_query_elements); - frames[i].timestamp_names.resize(max_timestamp_query_elements); - frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); - frames[i].timestamp_count = 0; - frames[i].timestamp_result_names.resize(max_timestamp_query_elements); - frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); - frames[i].timestamp_result_values.resize(max_timestamp_query_elements); - frames[i].timestamp_result_count = 0; - } + // Create query pool. + frames[i].timestamp_pool = driver->timestamp_query_pool_create(max_timestamp_query_elements); + frames[i].timestamp_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); + frames[i].timestamp_count = 0; + frames[i].timestamp_result_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_count = 0; } - { - // Begin the first command buffer for the first frame, so - // setting up things can be done in the meantime until swap_buffers(), which is called before advance. - bool ok = driver->command_buffer_begin(frames[0].setup_command_buffer); - ERR_FAIL_COND(!ok); + // Start from frame count, so everything else is immediately old. + frames_drawn = frames.size(); - ok = driver->command_buffer_begin(frames[0].draw_command_buffer); - ERR_FAIL_COND(!ok); - if (local_device.is_null()) { - context->set_setup_buffer(frames[0].setup_command_buffer); // Append now so it's added before everything else. - context->append_command_buffer(frames[0].draw_command_buffer); - } - } + // Initialize recording on the first frame. + driver->begin_segment(frame, frames_drawn++); + driver->command_buffer_begin(frames[0].setup_command_buffer); + driver->command_buffer_begin(frames[0].draw_command_buffer); - for (int i = 0; i < frame_count; i++) { - // Reset all queries in a query pool before doing any operations with them. + // Create draw graph and start it initialized as well. + draw_graph.initialize(driver, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME); + draw_graph.begin(); + + for (uint32_t i = 0; i < frames.size(); i++) { + // Reset all queries in a query pool before doing any operations with them.. driver->command_timestamp_query_pool_reset(frames[0].setup_command_buffer, frames[i].timestamp_pool, max_timestamp_query_elements); } + // Convert block size from KB. staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); staging_buffer_block_size = MAX(4u, staging_buffer_block_size); - staging_buffer_block_size *= 1024; // Kb -> bytes. + staging_buffer_block_size *= 1024; + + // Convert staging buffer size from MB. staging_buffer_max_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/max_size_mb"); staging_buffer_max_size = MAX(1u, staging_buffer_max_size); staging_buffer_max_size *= 1024 * 1024; @@ -4884,49 +5080,50 @@ void RenderingDevice::initialize(ApiContextRD *p_context, bool p_local_device) { // Validate enough blocks. staging_buffer_max_size = staging_buffer_block_size * 4; } + texture_upload_region_size_px = GLOBAL_GET("rendering/rendering_device/staging_buffer/texture_upload_region_size_px"); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); - frames_drawn = frame_count; // Start from frame count, so everything else is immediately old. - // Ensure current staging block is valid and at least one per frame exists. staging_buffer_current = 0; staging_buffer_used = false; - for (int i = 0; i < frame_count; i++) { + for (uint32_t i = 0; i < frames.size(); i++) { // Staging was never used, create a block. - Error err = _insert_staging_block(); + err = _insert_staging_block(); ERR_CONTINUE(err != OK); } draw_list = nullptr; - draw_list_count = 0; - compute_list = nullptr; - pipelines_cache_file_path = "user://vulkan/pipelines"; - pipelines_cache_file_path += "." + context->get_device_name().validate_filename().replace(" ", "_").to_lower(); - if (Engine::get_singleton()->is_editor_hint()) { - pipelines_cache_file_path += ".editor"; - } - pipelines_cache_file_path += ".cache"; + if (main_instance) { + // Only the instance that is not a local device and is also the singleton is allowed to manage a pipeline cache. + pipeline_cache_file_path = "user://vulkan/pipelines"; + pipeline_cache_file_path += "." + device.name.validate_filename().replace(" ", "_").to_lower(); + if (Engine::get_singleton()->is_editor_hint()) { + pipeline_cache_file_path += ".editor"; + } - Vector cache_data = _load_pipeline_cache(); - pipelines_cache_enabled = driver->pipeline_cache_create(cache_data); - if (pipelines_cache_enabled) { - pipelines_cache_size = driver->pipeline_cache_query_size(); - print_verbose(vformat("Startup PSO cache (%.1f MiB)", pipelines_cache_size / (1024.0f * 1024.0f))); + pipeline_cache_file_path += ".cache"; + + Vector cache_data = _load_pipeline_cache(); + pipeline_cache_enabled = driver->pipeline_cache_create(cache_data); + if (pipeline_cache_enabled) { + pipeline_cache_size = driver->pipeline_cache_query_size(); + print_verbose(vformat("Startup PSO cache (%.1f MiB)", pipeline_cache_size / (1024.0f * 1024.0f))); + } } - draw_graph.initialize(driver, frame_count, SECONDARY_COMMAND_BUFFERS_PER_FRAME); + return OK; } Vector RenderingDevice::_load_pipeline_cache() { - DirAccess::make_dir_recursive_absolute(pipelines_cache_file_path.get_base_dir()); + DirAccess::make_dir_recursive_absolute(pipeline_cache_file_path.get_base_dir()); - if (FileAccess::exists(pipelines_cache_file_path)) { + if (FileAccess::exists(pipeline_cache_file_path)) { Error file_error; - Vector file_data = FileAccess::get_file_as_bytes(pipelines_cache_file_path, &file_error); + Vector file_data = FileAccess::get_file_as_bytes(pipeline_cache_file_path, &file_error); return file_data; } else { return Vector(); @@ -4935,11 +5132,11 @@ Vector RenderingDevice::_load_pipeline_cache() { void RenderingDevice::_update_pipeline_cache(bool p_closing) { { - bool still_saving = pipelines_cache_save_task != WorkerThreadPool::INVALID_TASK_ID && !WorkerThreadPool::get_singleton()->is_task_completed(pipelines_cache_save_task); + bool still_saving = pipeline_cache_save_task != WorkerThreadPool::INVALID_TASK_ID && !WorkerThreadPool::get_singleton()->is_task_completed(pipeline_cache_save_task); if (still_saving) { if (p_closing) { - WorkerThreadPool::get_singleton()->wait_for_task_completion(pipelines_cache_save_task); - pipelines_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; + WorkerThreadPool::get_singleton()->wait_for_task_completion(pipeline_cache_save_task); + pipeline_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; } else { // We can't save until the currently running save is done. We'll retry next time; worst case, we'll save when exiting. return; @@ -4950,7 +5147,7 @@ void RenderingDevice::_update_pipeline_cache(bool p_closing) { { size_t new_pipelines_cache_size = driver->pipeline_cache_query_size(); ERR_FAIL_COND(!new_pipelines_cache_size); - size_t difference = new_pipelines_cache_size - pipelines_cache_size; + size_t difference = new_pipelines_cache_size - pipeline_cache_size; bool must_save = false; @@ -4962,7 +5159,7 @@ void RenderingDevice::_update_pipeline_cache(bool p_closing) { } if (must_save) { - pipelines_cache_size = new_pipelines_cache_size; + pipeline_cache_size = new_pipelines_cache_size; } else { return; } @@ -4971,7 +5168,7 @@ void RenderingDevice::_update_pipeline_cache(bool p_closing) { if (p_closing) { _save_pipeline_cache(this); } else { - pipelines_cache_save_task = WorkerThreadPool::get_singleton()->add_native_task(&_save_pipeline_cache, this, false, "PipelineCacheSave"); + pipeline_cache_save_task = WorkerThreadPool::get_singleton()->add_native_task(&_save_pipeline_cache, this, false, "PipelineCacheSave"); } } @@ -4987,7 +5184,7 @@ void RenderingDevice::_save_pipeline_cache(void *p_data) { } print_verbose(vformat("Updated PSO cache (%.1f MiB)", cache_blob.size() / (1024.0f * 1024.0f))); - Ref f = FileAccess::open(self->pipelines_cache_file_path, FileAccess::WRITE, nullptr); + Ref f = FileAccess::open(self->pipeline_cache_file_path, FileAccess::WRITE, nullptr); if (f.is_valid()) { f->store_buffer(cache_blob); } @@ -5122,10 +5319,15 @@ uint64_t RenderingDevice::limit_get(Limit p_limit) const { } void RenderingDevice::finalize() { + if (!frames.is_empty()) { + // Wait for all frames to have finished rendering. + _flush_and_stall_for_all_frames(); + } + + // Delete everything the graph has created. + draw_graph.finalize(); + // Free all resources. - - _flush(false); - _free_rids(render_pipeline_owner, "Pipeline"); _free_rids(compute_pipeline_owner, "Compute"); _free_rids(uniform_set_owner, "UniformSet"); @@ -5181,9 +5383,12 @@ void RenderingDevice::finalize() { _free_pending_resources(f); driver->command_pool_free(frames[i].command_pool); driver->timestamp_query_pool_free(frames[i].timestamp_pool); + driver->semaphore_free(frames[i].setup_semaphore); + driver->semaphore_free(frames[i].draw_semaphore); + driver->fence_free(frames[i].draw_fence); } - if (pipelines_cache_enabled) { + if (pipeline_cache_enabled) { _update_pipeline_cache(true); driver->pipeline_cache_free(); } @@ -5205,6 +5410,34 @@ void RenderingDevice::finalize() { } framebuffer_formats.clear(); + // Delete the swap chains created for the screens. + for (const KeyValue &it : screen_swap_chains) { + driver->swap_chain_free(it.value); + } + + screen_swap_chains.clear(); + + // Delete the command queues. + if (present_queue) { + if (main_queue != present_queue) { + // Only delete the present queue if it's unique. + driver->command_queue_free(present_queue); + } + + present_queue = RDD::CommandQueueID(); + } + + if (main_queue) { + driver->command_queue_free(main_queue); + main_queue = RDD::CommandQueueID(); + } + + // Delete the driver once everything else has been deleted. + if (driver != nullptr) { + context->driver_free(driver); + driver = nullptr; + } + // All these should be clear at this point. ERR_FAIL_COND(dependency_map.size()); ERR_FAIL_COND(reverse_dependency_map.size()); @@ -5212,7 +5445,7 @@ void RenderingDevice::finalize() { RenderingDevice *RenderingDevice::create_local_device() { RenderingDevice *rd = memnew(RenderingDevice); - rd->initialize(context, true); + rd->initialize(context); return rd; } @@ -5291,7 +5524,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("screen_get_width", "screen"), &RenderingDevice::screen_get_width, DEFVAL(DisplayServer::MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("screen_get_height", "screen"), &RenderingDevice::screen_get_height, DEFVAL(DisplayServer::MAIN_WINDOW_ID)); - ClassDB::bind_method(D_METHOD("screen_get_framebuffer_format"), &RenderingDevice::screen_get_framebuffer_format); + ClassDB::bind_method(D_METHOD("screen_get_framebuffer_format", "screen"), &RenderingDevice::screen_get_framebuffer_format, DEFVAL(DisplayServer::MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color())); @@ -5889,17 +6122,15 @@ void RenderingDevice::_bind_methods() { } RenderingDevice::~RenderingDevice() { - if (local_device.is_valid()) { - finalize(); - context->local_device_free(local_device); - } + finalize(); + if (singleton == this) { singleton = nullptr; } } RenderingDevice::RenderingDevice() { - if (singleton == nullptr) { // there may be more rendering devices later + if (singleton == nullptr) { singleton = this; } } diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 2ccef663083..f4b7683d6e2 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -64,36 +64,11 @@ class RenderingDevice : public RenderingDeviceCommons { _THREAD_SAFE_CLASS_ public: - enum DeviceFamily { - DEVICE_UNKNOWN, - DEVICE_OPENGL, - DEVICE_VULKAN, - DEVICE_DIRECTX, - }; - enum ShaderLanguage { SHADER_LANGUAGE_GLSL, SHADER_LANGUAGE_HLSL }; - enum SubgroupOperations { - SUBGROUP_BASIC_BIT = 1, - SUBGROUP_VOTE_BIT = 2, - SUBGROUP_ARITHMETIC_BIT = 4, - SUBGROUP_BALLOT_BIT = 8, - SUBGROUP_SHUFFLE_BIT = 16, - SUBGROUP_SHUFFLE_RELATIVE_BIT = 32, - SUBGROUP_CLUSTERED_BIT = 64, - SUBGROUP_QUAD_BIT = 128, - }; - - struct Capabilities { - // main device info - DeviceFamily device_family = DEVICE_UNKNOWN; - uint32_t version_major = 1; - uint32_t version_minor = 0; - }; - typedef int64_t DrawListID; typedef int64_t ComputeListID; @@ -110,9 +85,9 @@ private: static RenderingDevice *singleton; - Capabilities device_capabilities; - - RenderingDeviceDriver *driver = nullptr; // Owned by the context. + RenderingContextDriver *context = nullptr; + RenderingDeviceDriver *driver = nullptr; + RenderingContextDriver::Device device; protected: static void _bind_methods(); @@ -188,8 +163,8 @@ private: enum StagingRequiredAction { STAGING_REQUIRED_ACTION_NONE, - STAGING_REQUIRED_ACTION_FLUSH_CURRENT, - STAGING_REQUIRED_ACTION_FLUSH_OLDER + STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL, + STAGING_REQUIRED_ACTION_STALL_PREVIOUS }; Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment = true); @@ -819,12 +794,11 @@ private: Error _texture_copy_bind_compat_84976(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField p_post_barrier); Error _texture_clear_bind_compat_84976(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField p_post_barrier); Error _texture_resolve_multisample_bind_compat_84976(RID p_from_texture, RID p_to_texture, BitField p_post_barrier); + FramebufferFormatID _screen_get_framebuffer_format_bind_compat_87340() const; #endif public: - ApiContextRD *get_context() const { return context; } - - const Capabilities *get_device_capabilities() const { return &device_capabilities; }; + const RDD::Capabilities &get_device_capabilities() const { return driver->get_capabilities(); } bool has_feature(const Features p_feature) const; @@ -996,10 +970,10 @@ private: RID_Owner render_pipeline_owner; - bool pipelines_cache_enabled = false; - size_t pipelines_cache_size = 0; - String pipelines_cache_file_path; - WorkerThreadPool::TaskID pipelines_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; + bool pipeline_cache_enabled = false; + size_t pipeline_cache_size = 0; + String pipeline_cache_file_path; + WorkerThreadPool::TaskID pipeline_cache_save_task = WorkerThreadPool::INVALID_TASK_ID; Vector _load_pipeline_cache(); void _update_pipeline_cache(bool p_closing = false); @@ -1024,13 +998,22 @@ public: RID compute_pipeline_create(RID p_shader, const Vector &p_specialization_constants = Vector()); bool compute_pipeline_is_valid(RID p_pipeline); +private: /****************/ /**** SCREEN ****/ /****************/ + HashMap screen_swap_chains; + HashMap screen_framebuffers; - int screen_get_width(DisplayServer::WindowID p_screen = 0) const; - int screen_get_height(DisplayServer::WindowID p_screen = 0) const; - FramebufferFormatID screen_get_framebuffer_format() const; + uint32_t _get_swap_chain_desired_count() const; + +public: + Error screen_create(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID); + Error screen_prepare_for_drawing(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID); + int screen_get_width(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const; + int screen_get_height(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const; + FramebufferFormatID screen_get_framebuffer_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const; + Error screen_free(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID); /*************************/ /**** DRAW LISTS (II) ****/ @@ -1101,7 +1084,6 @@ private: DrawList *draw_list = nullptr; uint32_t draw_list_subpass_count = 0; - uint32_t draw_list_count = 0; RDD::RenderPassID draw_list_render_pass; RDD::FramebufferID draw_list_vkframebuffer; #ifdef DEBUG_ENABLED @@ -1214,6 +1196,15 @@ private: RenderingDeviceGraph draw_graph; + /**************************/ + /**** QUEUE MANAGEMENT ****/ + /**************************/ + + RDD::CommandQueueFamilyID main_queue_family; + RDD::CommandQueueFamilyID present_queue_family; + RDD::CommandQueueID main_queue; + RDD::CommandQueueID present_queue; + /**************************/ /**** FRAME MANAGEMENT ****/ /**************************/ @@ -1232,7 +1223,6 @@ private: // nature of the GPU. They will get deleted // when the frame is cycled. -private: struct Frame { // List in usage order, from last to free to first to free. List buffers_to_dispose_of; @@ -1245,13 +1235,30 @@ private: List compute_pipelines_to_dispose_of; RDD::CommandPoolID command_pool; + + // Used at the beginning of every frame for set-up. // Used for filling up newly created buffers with data provided on creation. // Primarily intended to be accessed by worker threads. - // Ideally this cmd buffer should use an async transfer queue. - RDD::CommandBufferID setup_command_buffer; // Used at the beginning of every frame for set-up. - // The main cmd buffer for drawing and compute. + // Ideally this command buffer should use an async transfer queue. + RDD::CommandBufferID setup_command_buffer; + + // The main command buffer for drawing and compute. // Primarily intended to be used by the main thread to do most stuff. - RDD::CommandBufferID draw_command_buffer; // Used at the beginning of every frame for set-up. + RDD::CommandBufferID draw_command_buffer; + + // Signaled by the setup submission. Draw must wait on this semaphore. + RDD::SemaphoreID setup_semaphore; + + // Signaled by the draw submission. Present must wait on this semaphore. + RDD::SemaphoreID draw_semaphore; + + // Signaled by the draw submission. Must wait on this fence before beginning + // command recording for the frame. + RDD::FenceID draw_fence; + bool draw_fence_signaled = false; + + // Swap chains prepared for drawing during the frame that must be presented. + LocalVector swap_chains_to_present; struct Timestamp { String description; @@ -1272,37 +1279,32 @@ private: uint32_t max_timestamp_query_elements = 0; - TightLocalVector frames; // Frames available, for main device they are cycled (usually 3), for local devices only 1. - int frame = 0; // Current frame. - int frame_count = 0; // Total amount of frames. + int frame = 0; + TightLocalVector frames; uint64_t frames_drawn = 0; - RID local_device; - bool local_device_processing = false; void _free_pending_resources(int p_frame); - ApiContextRD *context = nullptr; - uint64_t texture_memory = 0; uint64_t buffer_memory = 0; void _free_internal(RID p_id); - void _flush(bool p_current_frame); - - bool screen_prepared = false; + void _begin_frame(); + void _end_frame(); + void _execute_frame(bool p_signal_for_present); + void _present_frame(); + void _stall_for_previous_frames(); + void _flush_and_stall_for_all_frames(); template void _free_rids(T &p_owner, const char *p_type); - void _finalize_command_buffers(bool p_postpare); - void _begin_frame(); - #ifdef DEV_ENABLED HashMap resource_names; #endif public: - void initialize(ApiContextRD *p_context, bool p_local_device = false); + Error initialize(RenderingContextDriver *p_context, DisplayServer::WindowID p_main_window = DisplayServer::INVALID_WINDOW_ID); void finalize(); void free(RID p_id); @@ -1324,9 +1326,6 @@ public: uint64_t limit_get(Limit p_limit) const; - //methods below not exposed, used by RenderingDeviceRD - void prepare_screen_for_drawing(); - void swap_buffers(); uint32_t get_frame_delay() const; @@ -1352,6 +1351,7 @@ public: String get_device_vendor_name() const; String get_device_name() const; DeviceType get_device_type() const; + String get_device_api_name() const; String get_device_api_version() const; String get_device_pipeline_cache_uuid() const; diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index a8936f8cca1..688a6441a7b 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -820,6 +820,17 @@ public: SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS, }; + enum SubgroupOperations { + SUBGROUP_BASIC_BIT = 1, + SUBGROUP_VOTE_BIT = 2, + SUBGROUP_ARITHMETIC_BIT = 4, + SUBGROUP_BALLOT_BIT = 8, + SUBGROUP_SHUFFLE_BIT = 16, + SUBGROUP_SHUFFLE_RELATIVE_BIT = 32, + SUBGROUP_CLUSTERED_BIT = 64, + SUBGROUP_QUAD_BIT = 128, + }; + //////////////////////////////////////////// // PROTECTED STUFF // Not exposed by RenderingDevice, but shared diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 663222e69da..753b3668bc3 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -49,12 +49,11 @@ #include "core/object/object.h" #include "core/variant/type_info.h" #include "servers/display_server.h" +#include "servers/rendering/rendering_context_driver.h" #include "servers/rendering/rendering_device_commons.h" #include -class ApiContextRD; - // This may one day be used in Godot for interoperability between C arrays, Vector and LocalVector. // (See https://github.com/godotengine/godot-proposals/issues/5144.) template @@ -127,20 +126,22 @@ public: id(p_id) {} }; -#define DEFINE_ID(m_name) \ - struct m_name##ID : public ID { \ - _ALWAYS_INLINE_ operator bool() const { return id != 0; } \ - _ALWAYS_INLINE_ m_name##ID &operator=(m_name##ID p_other) { \ - id = p_other.id; \ - return *this; \ - } \ - _ALWAYS_INLINE_ bool operator<(const m_name##ID &p_other) const { return id < p_other.id; } \ - _ALWAYS_INLINE_ m_name##ID(const m_name##ID &p_other) : ID(p_other.id) {} \ - _ALWAYS_INLINE_ explicit m_name##ID(uint64_t p_int) : ID(p_int) {} \ - _ALWAYS_INLINE_ explicit m_name##ID(void *p_ptr) : ID((size_t)p_ptr) {} \ - _ALWAYS_INLINE_ m_name##ID() = default; \ - }; \ - /* Ensure type-punnable to pointer. Makes some things easier.*/ \ +#define DEFINE_ID(m_name) \ + struct m_name##ID : public ID { \ + _ALWAYS_INLINE_ operator bool() const { return id != 0; } \ + _ALWAYS_INLINE_ m_name##ID &operator=(m_name##ID p_other) { \ + id = p_other.id; \ + return *this; \ + } \ + _ALWAYS_INLINE_ bool operator<(const m_name##ID &p_other) const { return id < p_other.id; } \ + _ALWAYS_INLINE_ bool operator==(const m_name##ID &p_other) const { return id == p_other.id; } \ + _ALWAYS_INLINE_ bool operator!=(const m_name##ID &p_other) const { return id != p_other.id; } \ + _ALWAYS_INLINE_ m_name##ID(const m_name##ID &p_other) : ID(p_other.id) {} \ + _ALWAYS_INLINE_ explicit m_name##ID(uint64_t p_int) : ID(p_int) {} \ + _ALWAYS_INLINE_ explicit m_name##ID(void *p_ptr) : ID((size_t)p_ptr) {} \ + _ALWAYS_INLINE_ m_name##ID() = default; \ + }; \ + /* Ensure type-punnable to pointer. Makes some things easier.*/ \ static_assert(sizeof(m_name##ID) == sizeof(void *)); // Id types declared before anything else to prevent cyclic dependencies between the different concerns. @@ -148,14 +149,26 @@ public: DEFINE_ID(Texture); DEFINE_ID(Sampler); DEFINE_ID(VertexFormat); + DEFINE_ID(CommandQueue); + DEFINE_ID(CommandQueueFamily); DEFINE_ID(CommandPool); DEFINE_ID(CommandBuffer); + DEFINE_ID(SwapChain); DEFINE_ID(Framebuffer); DEFINE_ID(Shader); DEFINE_ID(UniformSet); DEFINE_ID(Pipeline); DEFINE_ID(RenderPass); DEFINE_ID(QueryPool); + DEFINE_ID(Fence); + DEFINE_ID(Semaphore); + +public: + /*****************/ + /**** GENERIC ****/ + /*****************/ + + virtual Error initialize(uint32_t p_device_index, uint32_t p_frame_count) = 0; /****************/ /**** MEMORY ****/ @@ -361,10 +374,44 @@ public: VectorView p_buffer_barriers, VectorView p_texture_barriers) = 0; + /****************/ + /**** FENCES ****/ + /****************/ + + virtual FenceID fence_create() = 0; + virtual Error fence_wait(FenceID p_fence) = 0; + virtual void fence_free(FenceID p_fence) = 0; + + /********************/ + /**** SEMAPHORES ****/ + /********************/ + + virtual SemaphoreID semaphore_create() = 0; + virtual void semaphore_free(SemaphoreID p_semaphore) = 0; + /*************************/ /**** COMMAND BUFFERS ****/ /*************************/ + // ----- QUEUE FAMILY ----- + + enum CommandQueueFamilyBits { + COMMAND_QUEUE_FAMILY_GRAPHICS_BIT = 0x1, + COMMAND_QUEUE_FAMILY_COMPUTE_BIT = 0x2, + COMMAND_QUEUE_FAMILY_TRANSFER_BIT = 0x4 + }; + + // The requested command queue family must support all specified bits or it'll fail to return a valid family otherwise. If a valid surface is specified, the queue must support presenting to it. + // It is valid to specify no bits and a valid surface: in this case, the dedicated presentation queue family will be the preferred option. + virtual CommandQueueFamilyID command_queue_family_get(BitField p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) = 0; + + // ----- QUEUE ----- + + virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) = 0; + virtual Error command_queue_execute(CommandQueueID p_cmd_queue, VectorView p_cmd_buffers, VectorView p_wait_semaphores, VectorView p_signal_semaphores, FenceID p_signal_fence) = 0; + virtual Error command_queue_present(CommandQueueID p_cmd_queue, VectorView p_swap_chains, VectorView p_wait_semaphores) = 0; + virtual void command_queue_free(CommandQueueID p_cmd_queue) = 0; + // ----- POOL ----- enum CommandBufferType { @@ -372,17 +419,39 @@ public: COMMAND_BUFFER_TYPE_SECONDARY, }; - virtual CommandPoolID command_pool_create(CommandBufferType p_cmd_buffer_type) = 0; + virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) = 0; virtual void command_pool_free(CommandPoolID p_cmd_pool) = 0; // ----- BUFFER ----- - virtual CommandBufferID command_buffer_create(CommandBufferType p_cmd_buffer_type, CommandPoolID p_cmd_pool) = 0; + virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) = 0; virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) = 0; virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) = 0; virtual void command_buffer_end(CommandBufferID p_cmd_buffer) = 0; virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView p_secondary_cmd_buffers) = 0; + /********************/ + /**** SWAP CHAIN ****/ + /********************/ + + // The swap chain won't be valid for use until it is resized at least once. + virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) = 0; + + // The swap chain must not be in use when a resize is requested. Wait until all rendering associated to the swap chain is finished before resizing it. + virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) = 0; + + // Acquire the framebuffer that can be used for drawing. This must be called only once every time a new frame will be rendered. + virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) = 0; + + // Retrieve the render pass that can be used to draw on the swap chain's framebuffers. + virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) = 0; + + // Retrieve the format used by the swap chain's framebuffers. + virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) = 0; + + // Wait until all rendering associated to the swap chain is finished before deleting it. + virtual void swap_chain_free(SwapChainID p_swap_chain) = 0; + /*********************/ /**** FRAMEBUFFER ****/ /*********************/ @@ -633,17 +702,11 @@ public: virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) = 0; virtual void command_end_label(CommandBufferID p_cmd_buffer) = 0; - /****************/ - /**** SCREEN ****/ - /****************/ - - virtual DataFormat screen_get_format() = 0; - /********************/ /**** SUBMISSION ****/ /********************/ - virtual void begin_segment(CommandBufferID p_cmd_buffer, uint32_t p_frame_index, uint32_t p_frames_drawn) = 0; + virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) = 0; virtual void end_segment() = 0; /**************/ @@ -682,6 +745,19 @@ public: SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH, }; + enum DeviceFamily { + DEVICE_UNKNOWN, + DEVICE_OPENGL, + DEVICE_VULKAN, + DEVICE_DIRECTX, + }; + + struct Capabilities { + DeviceFamily device_family = DEVICE_UNKNOWN; + uint32_t version_major = 1; + uint32_t version_minor = 0; + }; + virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) = 0; virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) = 0; virtual uint64_t get_total_memory_used() = 0; @@ -689,6 +765,10 @@ public: virtual uint64_t api_trait_get(ApiTrait p_trait); virtual bool has_feature(Features p_feature) = 0; virtual const MultiviewCapabilities &get_multiview_capabilities() = 0; + virtual String get_api_name() const = 0; + virtual String get_api_version() const = 0; + virtual String get_pipeline_cache_uuid() const = 0; + virtual const Capabilities &get_capabilities() const = 0; /******************/ diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index 904b439e652..83fb2d19182 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -40,15 +40,6 @@ RenderingDeviceGraph::RenderingDeviceGraph() { } RenderingDeviceGraph::~RenderingDeviceGraph() { - _wait_for_secondary_command_buffer_tasks(); - - for (Frame &f : frames) { - for (SecondaryCommandBuffer &secondary : f.secondary_command_buffers) { - if (secondary.command_pool.id != 0) { - driver->command_pool_free(secondary.command_pool); - } - } - } } bool RenderingDeviceGraph::_is_write_usage(ResourceUsage p_usage) { @@ -1246,7 +1237,7 @@ void RenderingDeviceGraph::_print_compute_list(const uint8_t *p_instruction_data } } -void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, uint32_t p_secondary_command_buffers_per_frame) { +void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) { driver = p_driver; frames.resize(p_frame_count); @@ -1255,8 +1246,8 @@ void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, uin for (uint32_t j = 0; j < p_secondary_command_buffers_per_frame; j++) { SecondaryCommandBuffer &secondary = frames[i].secondary_command_buffers[j]; - secondary.command_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY); - secondary.command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY, secondary.command_pool); + secondary.command_pool = driver->command_pool_create(p_secondary_command_queue_family, RDD::COMMAND_BUFFER_TYPE_SECONDARY); + secondary.command_buffer = driver->command_buffer_create(secondary.command_pool); secondary.task = WorkerThreadPool::INVALID_TASK_ID; } } @@ -1264,6 +1255,20 @@ void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, uin driver_honors_barriers = driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS); } +void RenderingDeviceGraph::finalize() { + _wait_for_secondary_command_buffer_tasks(); + + for (Frame &f : frames) { + for (SecondaryCommandBuffer &secondary : f.secondary_command_buffers) { + if (secondary.command_pool.id != 0) { + driver->command_pool_free(secondary.command_pool); + } + } + } + + frames.clear(); +} + void RenderingDeviceGraph::begin() { command_data.clear(); command_data_offsets.clear(); diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index 0d48f0491b2..995fdb27d1b 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -619,7 +619,8 @@ private: public: RenderingDeviceGraph(); ~RenderingDeviceGraph(); - void initialize(RDD *p_driver, uint32_t p_frame_count, uint32_t p_secondary_command_buffers_per_frame); + void initialize(RDD *p_driver, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame); + void finalize(); void begin(); void add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size); void add_buffer_copy(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, RDD::BufferCopyRegion p_region);